<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Benjamin Tari's blog]]></title><description><![CDATA[Benjamin Tari's blog]]></description><link>https://blog.benjamintari.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 02 Jun 2026 16:45:07 GMT</lastBuildDate><atom:link href="https://blog.benjamintari.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Send scheduled messages from a bot to a channel in Microsoft Teams]]></title><description><![CDATA[A common task in creating a bot for Microsoft Teams is sending messages from the bot to members of a channel on a specified schedule. While there are many great examples in python version of botbuilder respository such as Starting a new thread in a c...]]></description><link>https://blog.benjamintari.com/send-scheduled-messages-from-a-bot-to-a-channel-in-microsoft-teams</link><guid isPermaLink="true">https://blog.benjamintari.com/send-scheduled-messages-from-a-bot-to-a-channel-in-microsoft-teams</guid><category><![CDATA[Microsoft Teams features]]></category><category><![CDATA[bots]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Mohammad Tari]]></dc:creator><pubDate>Sun, 15 Sep 2024 17:20:01 GMT</pubDate><content:encoded><![CDATA[<p>A common task in creating a bot for Microsoft Teams is sending messages from the bot to members of a channel on a specified schedule. While there are many great examples in python version of <a target="_blank" href="https://github.com/microsoft/BotBuilder-Samples">botbuilder respository</a> such as <a target="_blank" href="https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/python">Starting a new thread in a channel</a>, some extra steps are necessary to adapt the samples to our use case. The previous example triggers the bot with <code>on_message_activity</code>, which is not what we are looking for.</p>
<p>I will use <a target="_blank" href="https://docs.celeryq.dev/en/main/index.html">Celery</a> as a task scheduler for sending a message. I will also use <a target="_blank" href="https://github.com/Microsoft/botbuilder-python">botbuilder connector</a> to be able to connect to Microsoft API for Teams outside of our bot controller. Here is the the list of libraries that we need:</p>
<pre><code class="lang-python">botbuilder-core==<span class="hljs-number">4.14</span><span class="hljs-number">.6</span>
celery==<span class="hljs-number">5.3</span><span class="hljs-number">.4</span>
</code></pre>
<p>Another challenge is that while sending a message to channel using Microsoft Graph API with delegated permission is possible, currently the same task with application type permission is not supported. Therefore we borrow the following code from <a target="_blank" href="https://pypi.org/project/botframework-connector/">botframework-connector</a> and modify it so that it is suitable for Microsoft Teams channels:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> botbuilder.schema <span class="hljs-keyword">import</span> ConversationParameters, ChannelAccount
<span class="hljs-keyword">from</span> botframework.connector <span class="hljs-keyword">import</span> ConnectorClient
<span class="hljs-keyword">from</span> botframework.connector.auth <span class="hljs-keyword">import</span> MicrosoftAppCredentials
<span class="hljs-keyword">from</span> celery <span class="hljs-keyword">import</span> Celery


app = Celery(<span class="hljs-string">'tasks'</span>, broker=<span class="hljs-string">'&lt;broker-address&gt;'</span>)

APP_ID = <span class="hljs-string">'&lt;your-app-id&gt;'</span>
APP_PASSWORD = <span class="hljs-string">'&lt;your-app-password&gt;'</span>
SERVICE_URL = <span class="hljs-string">'https://smba.trafficmanager.net/teams/'</span>
CHANNEL_ID = <span class="hljs-string">'msteams'</span>
BOT_ID = <span class="hljs-string">'&lt;bot-id&gt;'</span> <span class="hljs-comment"># for MS Teams app, it's '28:APP_ID'</span>
TEAMS_CHANNEL_ID = <span class="hljs-string">'&lt;channel-id&gt;'</span>

credentials = MicrosoftAppCredentials(APP_ID, APP_PASSWORD)
connector = ConnectorClient(credentials, base_url=SERVICE_URL)

<span class="hljs-meta">@app.task</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_message_to_teams</span>():</span>
    message = MessageFactory.text(<span class="hljs-string">"Hello World!"</span>)
    conversation = connector.conversations.create_conversation(
        ConversationParameters(
            bot=ChannelAccount(id=BOT_ID),
            channel_data={<span class="hljs-string">"channel"</span>: {<span class="hljs-string">"id"</span>: TEAMS_CHANNEL_ID}},
            activity=message,
        )
    )
</code></pre>
<p>There is another method to send a conversation to a channel called <code>connector.conversations.send_to_conversation()</code> which does not work currently with channels in Microsoft Teams, so instead we add the <code>activity</code> argument to <code>connector.conversations.create_conversation()</code> so that it sends the message at the same time that it creates the conversation.</p>
<p>We also need the unique identifier for the channel that we want to send the messages (<code>TEAMS_CHANNEL_ID</code>). An easy way to find this ID is to right click on the channel and select <strong>Get Link to channel</strong> on our Teams app, and get the URL-encoded version. <a target="_blank" href="https://learn.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0">Microsoft Graph API</a> provides the list of teams and channel as well:</p>
<pre><code class="lang-python">url = https://graph.microsoft.com/v1
</code></pre>
<pre><code class="lang-http"><span class="hljs-attribute">GET /teams</span>
</code></pre>
<pre><code class="lang-http"><span class="hljs-attribute">GET /teams/{team-id}/channels</span>
</code></pre>
<p>I will configure the schedule in Celery beat so that it send the message every Monday morning at 7:30 a.m.:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> celery.schedules <span class="hljs-keyword">import</span> crontab


app.conf.beat_schedule = {
    <span class="hljs-string">'send-message-to-teams'</span>: {
        <span class="hljs-string">'task'</span>: <span class="hljs-string">'tasks.send_message_to_teams'</span>,
        <span class="hljs-string">'schedule'</span>: crontab(hour=<span class="hljs-number">7</span>, minute=<span class="hljs-number">30</span>, day_of_week=<span class="hljs-number">1</span>),
    },
}
</code></pre>
<p>Finally, we need to run a Celery beat application to send the task to the worker:</p>
<pre><code class="lang-python">$ celery -A tasks beat --loglevel=INFO
</code></pre>
<p>and a Celery worker that simply executes the task on our desired time:</p>
<pre><code class="lang-python">$ celery -A tasks worker --loglevel=INFO
</code></pre>
<p>If we run the task manually from our application, we get the message from our bot in the channel!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726420543497/6fc8f477-f167-4430-a8b6-7b9bc97538ea.webp" alt class="image--center mx-auto" /></p>
]]></content:encoded></item></channel></rss>