Create A Microsoft Teams Bot

by IoFAdmin at

python | programming

Let's Create A Bot To Post Notifications To Our MS Teams Channel

We use MS Teams at my day job so I thought it would be fun to create a bot that could post notifications to a channel. Now that I have an army of bots at my command, I'll show you how to do the same.

What Will Our Bot Do?

I'm sure that you've seen all of the motivational posters that tell you to "Soar with the eagles!" and "Hang in there!" while walking around your office. Today, we're going to take the opposite approach by posting demotivational quotes in Teams.

Tools of the Trade AKA PIP Is Your Friend

To help us truly demotivate our co-workers, we're going to need a few things:

  • ChatGPT: our AI buddy that'll generate some quotes
  • Pony ORM: our database object relational mapping tool
  • MS Teams: we'll need a channel for our posts

With the Power of AI...

I'm not going to explain how to use ChatGPT because there are many good tutorials out there to get you started. Once you've created an account and get logged in, enter the following prompt:

generate a list of demotivational quotes

That prompt generated 20 quotes so I ran it again for a total of 40 quotes. Save those quotes somewhere because we'll need them soon.

Create A Teams Webhook

Setting up Teams to accept our posts is pretty easy if you follow the steps below.

  • Decide what channel you'd like to post in and create it if needed
  • Click on the Settings three dots of the channel and then select Connections
  • Find the "webhooks" in the search bar
  • Select Add or modify an "Incoming Webhook"
  • Add or save it
  • Re-open the connectors for the channel under Settings (if it closed)
  • Click "Configure" for the webhook
  • In the configuration box, fill in the name of your bot and add a profile picture (optional)
  • Click "Create"
  • Copy the newly created URL for your bot
  • Save the URL (we'll need it soon)
  • Click "Done"
  • Open the channel in Teams and you should see a message showing that you configured an incoming Webhook

Python To the Rescue

Now comes the fun part: our Python code! Open up your favorite editor and let's get started.

Install Pony ORM

Like most Python packages, you can easily install Pony with PIP by running the following command in your terminal:

pip install pony

Note: you might run in to problems if you're running Python 3.11 because currently Pony supports 3.10 and below. (Hopefully 3.11 support is coming soon.)

Create Our Quotes

Create a new Python file named create_db.py and put the following code in it:

from pony.orm import *
from Quote import Quote

db = Database()

@db_session
def doInserts():
    Quote(content="Everything happens for a reason. Sometimes the reason is you're stupid and make bad decisions.", person='Marion G. Harmon')
    Quote(content="Every dead body on Mt. Everest was once a highly motivated person, so… maybe calm down.", person='')
    Quote(content="Light travels faster than sound. This is why some people appear bright until you hear them speak.", person='Alan Dundes')
    Quote(content="Just because we accept you as you are doesn't mean we've abandoned hope you'll improve.", person='')
    Quote(content="Idiocy - never underestimate the power of stupid people in large groups.", person='')
    Quote(content="If life doesn't break you today, don't worry. It will try again tomorrow.", person='')
    Quote(content="People who say they'll give 110% don't understand how percentages work.", person='')
    Quote(content="A thousand-mile journey starts with one step. Then again, so does falling in a ditch and breaking your neck.", person='')
    Quote(content="Two things are infinite: the universe and human stupidity; and I'm not sure about the universe.", person='Albert Einstein')
    Quote(content="If you never try anything new, you'll miss out on many of life's great disappointments.", person='')
    Quote(content="If at first, you don't succeed, try, try again. Then quit. No use being a damn fool about it.", person='W.C. Fields')
    Quote(content="It could be that your purpose in life is to serve as a warning to others.", person='Ashleigh Brilliant')
    Quote(content="Today is the first day of the rest of your life. But so was yesterday, and look how that turned out.", person='')
    Quote(content="Just because you are unique doesn't mean you are useful.", person='')
    Quote(content="Oh, you hate your job? Why didn't you say so? There's a support group for that. It's called EVERYBODY, and they meet at the bar.", person='Drew Carey')
    Quote(content="I am free of all prejudice. I hate everyone equally.", person='W.C. Fields')
    Quote(content="Always remember that you are absolutely unique. Just like everyone else.", person='Margaret Mead')
    Quote(content="Multitasking - the art of doing twice as much as you should half as well as you could.", person='')
    Quote(content="The story so far: In the beginning, the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move.", person='Douglas Adams')
    Quote(content="Life is pain. Anyone who says otherwise is selling something.", person='William Goldman')
    Quote(content="If you want to know what God thinks of money, just look at the people he gave it to.", person='Dorothy Parker')
    Quote(content="When life knocks you down, stay there and take a nap.", person='')
    Quote(content="Nothing says \"you're a loser\" more than owning a motivational poster about being a winner.", person='')
    Quote(content="Not everything is a lesson. Sometimes you just fail.", person='Dwight Schrute')
    Quote(content="Fate is like a strange, unpopular restaurant filled with odd little waiters who bring you things you never asked for and don't always like.", person='Lemony Snicket')
    Quote(content="The worst part of success is trying to find someone who is happy for you.", person='Bette Midler')
    Quote(content="Your life can't fall apart if you never had it together.", person='')
    Quote(content="Doing nothing is very hard to do… you never know when you're finished.", person='')
    Quote(content="The road to success is always under construction.", person='Lily Tomlin')
    Quote(content="The reward for good work is more work.", person='Francesca Elisia')
    Quote(content="There are no stupid questions, but there are a LOT of inquisitive idiots.", person='')
    Quote(content="Go ahead and take risks - it gives the rest of us something to laugh at.", person='')
    Quote(content="Eagles may soar, but weasels don't get sucked into jet engines.", person='John Benfield')
    Quote(content="Raise your hand if you have had quite enough unsolicited advice about what should be done with any lemons that life may or may not give you.", person='')
    Quote(content="There's always someone on Youtube that can do it better than you.", person='')
    Quote(content="It's only when you look at an ant through a magnifying glass on a sunny day that you realize how often they burst into flames.", person='Harry Hill')
    Quote(content="Every day is Friday when you're unemployed.", person='')
    Quote(content="You're naturally funny because your life is a joke.", person='')
    Quote(content="The meaning of life is to find your gift. So good luck with that.", person='')
    Quote(content="Challenging yourself... is a good way to fail.", person='Dom Mazzetti')
    Quote(content="Why aim for the stars when you can comfortably settle for the couch?", person='')
    Quote(content="Dream big, then promptly forget about it and return to your mediocre existence.", person='')
    Quote(content="Procrastination: because there's always tomorrow to not get things done.", person='')
    Quote(content="Why try when failure is so much easier to achieve?", person='')
    Quote(content="The only thing standing between you and success is your complete lack of talent.", person='')
    Quote(content="Why be motivated when you can just blame everyone else for your problems?", person='')
    Quote(content="If at first you don't succeed, quit. It's easier that way.", person='')
    Quote(content="Don't worry about setting goals; they'll only remind you of how far you haven't come.", person='')
    Quote(content="You can achieve anything you don't put your mind to.", person='')
    Quote(content="Success is overrated. Embrace the comfort of mediocrity.", person='')
    Quote(content="Remember, the path to failure is paved with good intentions.", person='')
    Quote(content="Don't bother taking risks; just stay in your comfort zone and stagnate.", person='')
    Quote(content="Opportunity knocks once, but it usually just walks right past you.", person='')
    Quote(content="Hard work pays off, but laziness is so much more appealing.", person='')
    Quote(content="Don't aim for perfection; settle for 'good enough.", person='')
    Quote(content="Why strive for greatness when mediocrity is so much more comfortable?", person='')
    Quote(content="Motivation is for the ambitious. Embrace your lack of ambition.", person='')
    Quote(content="Failure is not just an option; it's practically inevitable.", person='')
    Quote(content="Success is fleeting, but failure? It's a lifelong companion.", person='')
    Quote(content="Don't bother trying to stand out; conformity is so much easier.", person='')

doInserts()

So what's this code doing? I'm glad you asked!

First we import Pony ORM and our Quote class (that we'll write in a moment). Next, we instantiate a Database object that Pony will use to save our data.

Now we create our doInserts function using the @db_session decorator. Your can read more about that in the Pony ORM documentation. Here we're creating our Quote objects and inserting them into our database.

Observant readers will be saying "what database?" Don't worry because we'll handle that soon.

Our Quote Class

Create another Python file named Quote.py with the following code:

from pony.orm import *

db = Database()

class Quote(db.Entity):
    content = Required(str)
    person = Optional(str)

    @db_session
    def showAll():
        data = select(q for q in Quote)

        for q in data:
            print(f'{q.content} - {q.person if q.person else "unknown"}')

    @db_session
    def randomQuote():
        data = select(q for q in Quote).random(1)[0]

        return f'{data.content} - {data.person if data.person else "unknown"}'

    @db_session
    def findById(id):
        data = Quote[id]

        return f'"{data.content}" - {data.person if data.person else "unknown"}'

db.bind(provider='sqlite', filename='quotes.sqlite', create_db=True)
db.generate_mapping(create_tables=True)

I won't go too in depth with this because SQL is out of scope for this tutorial and Pony has pretty good documentation.

After importing Pony ORM, we define our Quote database table. We have two columns: "content", which is a required string datafield, and "person", which is an optional string datafield.

Now we create our three methods that will interact with our database: showAll, randomQuote, and findById.

Our first method, showAll, queries our database for all Quote records and prints them out to the terminal. We'll just be using this for debugging purposes.

In randomQuote, we're randomly selecting a Quote record and returning it as an f-string.

Our final method, findById, allows us to query by our primary key and get back an f-string representation of that Quote record.

Lastly, we create our sqlite database named quotes.sqlite to save our Quote records.

Posting To Teams

Create TeamsPost.py and add this code:

import requests

class TeamsPost:
    url = 'YOUR_WEBHOOK_HERE'

    def sendTeams(self, content:str, title:str, color:str="000000") -> int:
        """
        - Send a teams notification to the desired webhook_url
        - Returns the status code of the HTTP request
            - webhook_url : the url you got from the teams webhook configuration
            - content : your formatted notification content
            - title : the message that'll be displayed as title, and on phone notifications
            - color (optional) : hexadecimal code of the notification's top line color, default corresponds to black
        """
        response = requests.post(
            url=self.url,
            headers={"Content-Type": "application/json"},
            json={
                "themeColor": color,
                "summary": title,
                "sections": [{
                    "activityTitle": title,
                    "activitySubtitle": content
                }],
            },
        )
        
        return response.status_code

The class uses Python's request package to POST a JSON payload to our webhook URL that we saved from Teams. (Don't forget to use your webhook URL in the "url" variable.) As long as our webhook URL is correct and we set up Teams, we'll be POSTing.

Last One I Promise

Create our last file named post_quote.py and add this:

from pony.orm import *
from Quote import Quote
from TeamsPost import TeamsPost

def main(quoteId):
    quote = Quote.findById(quoteId)

    post = TeamsPost()
    post.sendTeams(quote, 'Daily Demotivational Quote', '00FF00')

main(1)

This very simple function is sending an id (the primary key) to Quote's findById method. After querying the database for that id, we get back a Quote object. Finally, we pass that object to our sendTeams method which POSTs it to our channel. Note: you will have to manually change the id that's sent to the main function.

Running Our Code

To actually post our demotivational quote to MS Teams, we need to set the quote id and the run post_quote.py.