| description | Send a reply to a Teams channel thread. Remembers channels by name so you never need to paste the URL twice. |
|---|
$ARGUMENTS
Post a reply to a Teams channel thread via the Microsoft Graph API. Channels and threads are remembered in MemPalace so the user can reference them by short name (e.g., "Urgent Incidents") instead of pasting URLs every time.
The user may reference the target in three ways (check in this order):
If $ARGUMENTS contains a Teams URL (teams.microsoft.com/l/message/...):
/usr/bin/python3 ~/.config/opencode/skills/reading-teams/scripts/teams.py parse-url "<URL>"Extract team_id, channel_id, and thread_id from the output.
Save the channel to MemPalace (see "Saving channels" below).
If $ARGUMENTS mentions a channel by name (e.g., "Urgent Incidents", "General"):
mempalace_search(query="<channel_name>", wing="teams-channels")
Use the team_id, channel_id, and thread_id from the best matching drawer.
If the message has no URL and no recognizable channel name, search for the most recent channel:
mempalace_search(query="last-used", wing="teams-channels", room="last-used")
If nothing is found, ask the user which channel to use.
After resolving a channel (from URL or first use), save it for future reference:
mempalace_add_drawer(
wing="teams-channels",
room="saved",
content="CHANNEL:<friendly_name>|team:<team_id>|channel:<channel_id>|thread:<thread_id>"
)
Also update the last-used pointer:
mempalace_add_drawer(
wing="teams-channels",
room="last-used",
content="LAST-USED|<friendly_name>|team:<team_id>|channel:<channel_id>|thread:<thread_id>"
)
The friendly_name is derived from the URL parameter channelName (URL-decoded) or from the
user's text. Strip emoji and normalize to a short, searchable label (e.g., "Urgent Incidents").
Thread update: If the user provides a new URL for an already-saved channel, update the
thread_id in both saved and last-used rooms. The thread is the most recent conversation
the user is interacting with.
Wrap the user's text in proper HTML for Teams rendering. Use <br> for line breaks.
If the message references a person (by name, username, or explicit @mention):
- Look up the user via Graph API:
TOKEN=$(cat ~/.cache/teams-token.json | /usr/bin/python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
curl -s "https://graph.microsoft.com/v1.0/users?\$filter=startswith(mailNickname,'<username>')&\$select=id,displayName,mail" \
-H "Authorization: Bearer $TOKEN"-
If multiple results, ask the user to disambiguate.
-
Include the mention in the request body:
{
"body": {
"contentType": "html",
"content": "Message text <at id=\"0\">Display Name</at> more text"
},
"mentions": [
{
"id": 0,
"mentionText": "Display Name",
"mentioned": {
"user": {
"id": "<user_id>",
"displayName": "Display Name",
"userIdentityType": "aadUser"
}
}
}
]
}Before sending, ensure the Graph token is valid:
/usr/bin/python3 ~/.config/opencode/skills/reading-teams/scripts/teams.py tokenTOKEN=$(cat ~/.cache/teams-token.json | /usr/bin/python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
curl -s -X POST \
"https://graph.microsoft.com/beta/teams/<team_id>/channels/<channel_id>/messages/<thread_id>/replies" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '<json_body>'- Use
/usr/bin/python3(system Python) — the project.tool-versionsblocks the asdf shim. - Use
https://graph.microsoft.com/beta/—/v1.0/returns 403 for channel messages. - The token key in
~/.cache/teams-token.jsonistoken(notaccess_token). - Always use
contentType: "html"for the message body. - Send directly — do NOT ask for confirmation (the user already requested to send).
After successful send, report:
Publicado en <channel_name> (thread <thread_id>). Message ID: <id>
If the API returns an error, show the error and suggest running az login if it's auth-related.