-
Notifications
You must be signed in to change notification settings - Fork 5
add azurebot example #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
add azurebot example #263
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| --- | ||
| title: Azure Pipelines Bot for Microsoft Teams | ||
| description: Monitor Azure DevOps build completions and enable interactive pipeline management through Microsoft Teams conversations | ||
| integrations: ["azurebot"] | ||
| categories: ["DevOps"] | ||
| tags: | ||
| [ | ||
| "webhook", | ||
| "interactive_workflows", | ||
| "user_interactions", | ||
| "subscribe", | ||
| "next_event", | ||
| "event_loops", | ||
| ] | ||
| --- | ||
|
|
||
| # Azure Pipelines Bot | ||
|
|
||
| [](https://app.autokitteh.cloud/template?name=azure_pipelines_bot) | ||
|
|
||
| This automation connects Azure DevOps pipelines to Microsoft Teams, enabling teams to monitor build completions and interact with pipelines directly from Teams conversations. When a build completes, the bot posts a notification to a Teams channel and allows users to retry failed builds through simple chat commands. | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. Receives webhook notifications when Azure DevOps builds complete | ||
| 2. Posts build completion messages to a Microsoft Teams channel | ||
| 3. Listens for user interactions in the Teams conversation | ||
| 4. Allows users to retry builds by typing "retry" in response to build notifications | ||
| 5. Queues new builds with the same parameters as the original failed build | ||
|
|
||
| ## Cloud Usage | ||
|
|
||
| 1. Initialize your Azure Bot connection | ||
| 2. Set these required project variables: | ||
| - `CHANNEL_CONVO_ID` - Microsoft Teams channel conversation ID | ||
| - `AZURE_DEVOPS_PAT` - Azure DevOps Personal Access Token (marked as secret) | ||
| - `AZURE_DEVOPS_ORG` - Azure DevOps organization name | ||
| 3. Deploy project | ||
| 4. Configure Azure DevOps webhook to point to the generated webhook URL | ||
|
|
||
| ## Trigger Workflow | ||
|
|
||
| The workflow is triggered by Azure DevOps webhooks when builds complete with `eventType='build.complete'`. Users can then interact with the bot by: | ||
|
|
||
| - Typing `@<Bot Name> retry` in response to build completion messages to queue a new build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # This YAML file is a declarative manifest that describes the setup | ||
| # of an AutoKitteh project that demonstrates how to poll for new emails | ||
| version: v1 | ||
|
|
||
| project: | ||
| name: azure_pipelines_bot | ||
|
|
||
| vars: | ||
| - name: CHANNEL_CONVO_ID | ||
| value: "" | ||
| - name: AZURE_DEVOPS_PAT | ||
| value: "" | ||
| secret: true | ||
| - name: AZURE_DEVOPS_ORG | ||
| value: "" | ||
|
|
||
| connections: | ||
| - name: bot | ||
| integration: azurebot | ||
|
|
||
| triggers: | ||
| - name: azuredev_webhook | ||
| type: webhook | ||
| call: program.py:on_azuredev_webhook | ||
| event_type: POST | ||
| filter: data.body.json.eventType='build.complete' | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """MS Azure DevOps Pipelines -> MS Teams via Azure Bot""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from os import getenv | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from autokitteh import subscribe, next_event | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from autokitteh.azurebot import azurebot_client | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from azure.devops.connection import Connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from azure.devops.v7_1.build.models import Build | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from msrest.authentication import BasicAuthentication | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _CHANNEL_CONVO_ID = getenv("CHANNEL_CONVO_ID") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devops_clients = Connection( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| base_url=f"https://dev.azure.com/{getenv('AZURE_DEVOPS_ORG')}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| creds=BasicAuthentication("", getenv("AZURE_DEVOPS_PAT")), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).clients | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| build_client = devops_clients.get_build_client() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bot_client = azurebot_client("bot") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def on_azuredev_webhook(event): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data = event.data.body.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activity_id = bot_client.send_conversation_activity( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "message", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "text": data["detailedMessage"]["markdown"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| conversation_id=_CHANNEL_CONVO_ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )["id"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| convo_id = f"{_CHANNEL_CONVO_ID};messageid={activity_id}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| build_id, project_name = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data["resource"]["id"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data["resource"]["project"]["name"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Build completed: {build_id} in {project_name}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sub = subscribe("bot", filter=f"data.conversation.id='{convo_id}'") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while True: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| evt = next_event(sub) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(evt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not hasattr(evt, "text") or evt.text is None: | |
| continue |
Copilot
AI
Aug 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The infinite loop lacks an exit condition, which could lead to resource exhaustion or difficulty in stopping the process. Consider adding a timeout, maximum iteration count, or explicit break condition for scenarios like conversation inactivity or specific commands.
| while True: | |
| evt = next_event(sub) | |
| print(evt) | |
| text = evt.text.lower() | |
| import time | |
| INACTIVITY_TIMEOUT = 300 # seconds (5 minutes) | |
| last_event_time = time.time() | |
| while True: | |
| # Try to get the next event, with a timeout for inactivity | |
| try: | |
| evt = next_event(sub) | |
| last_event_time = time.time() | |
| except Exception as e: | |
| print(f"Error receiving event: {e}") | |
| break | |
| print(evt) | |
| text = evt.text.lower() | |
| # Exit loop if user sends "exit" or "quit" | |
| if "exit" in text or "quit" in text: | |
| bot_client.send_conversation_activity( | |
| {"type": "message", "text": "Conversation ended by user."}, | |
| conversation_id=convo_id, | |
| ) | |
| break | |
| # Check for inactivity timeout | |
| if time.time() - last_event_time > INACTIVITY_TIMEOUT: | |
| bot_client.send_conversation_activity( | |
| {"type": "message", "text": "Conversation ended due to inactivity."}, | |
| conversation_id=convo_id, | |
| ) | |
| break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment describes polling for emails, but this project is about Azure DevOps build notifications and Teams integration. Update the comment to accurately describe this project's purpose.