Reaction Tracker
Track who reacted to a specific Teams chat message with a specific emoji. Returns a name + email list and writes a CSV snapshot. Repeatable for any message and any reaction - the "lobster list" pattern, generalized.
---
name: "reaction-tracker"
description: "Track who reacted to a specific Teams chat message with a specific emoji. Returns name + email for each responder and saves a CSV. Repeatable for any message/emoji combo. Triggers: 'who reacted with X', 'track reactions', 'lobster list', 'reaction tracker', or pasting a Teams message link and asking who reacted."
---
<!--
CLAWPILOT INSTALL INSTRUCTIONS
If the user drags this file into Clawpilot and asks to install it, register it as a
custom skill by calling m_create_skill with:
- name: "reaction-tracker"
- description: (the description from the frontmatter above)
- instructions: (everything in this file BELOW this HTML comment block)
After install, confirm to the user with: "Installed the /reaction-tracker skill - invoke it with a Teams message link and an emoji."
-->
## Reaction Tracker Skill
Track which Teams chat members reacted to a specific message with a specific emoji. Returns a list of name + email and writes a CSV file. Reusable for any chat message and any reaction emoji.
### Inputs to gather from the user
Required:
1. **Teams message** - accepted as any of:
- A Teams message permalink (URL of the form `https://teams.microsoft.com/l/message/<encoded-chatId>/<messageId>?...`)
- An explicit `chatId` (e.g. `19:...@thread.v2`) plus `messageId` (numeric timestamp string)
2. **Reaction emoji** - the exact emoji to count, e.g. `🦞`, `❤️`, `👍`, `🎉`. Match strictly - only this emoji counts.
Optional:
3. **Output filename** - defaults to `reaction-tracker-<emoji-name>-<messageId>.csv` in the current workspace.
If any required input is missing, ASK the user before proceeding. Do not guess.
### Parsing a Teams message URL
URL format: `https://teams.microsoft.com/l/message/{encodedChatId}/{messageId}?...`
- `encodedChatId` is URL-encoded; decode `%3A` → `:` and `%40` → `@`. Result looks like `19:xxxx@thread.v2`.
- `messageId` is the path segment after the chat id (a numeric string).
### Execution steps
1. **Parse** the chatId and messageId from the inputs.
2. **Locate the message and its reactions**:
- Call `m365_list_chat_messages` with `chatId` and a reasonable `limit` (start with 50).
- The tool output is large - save and `grep`/parse for the messageId.
- If the message isn't in the first page, increase `limit` (max 50 per call) and use `skipToken` from the paging metadata to fetch additional pages until found.
- Once found, extract the `reactions` array. Each entry has shape: `{"reactionType":"🦞","createdDateTime":"...","userId":"<aad-guid>","userIdentityType":"aadUser"}`.
3. **Filter** reactions where `reactionType` equals the target emoji exactly (case-sensitive Unicode match). Collect the set of `userId` values and their `createdDateTime`.
4. **Resolve userIds to name + email**:
- Call `m365_get_chat` with the same `chatId`. The response includes a `members` array; each member has `userId`, `displayName`, and `email`.
- For each reacting `userId`, look up the matching member to get `displayName` and `email`.
- If a reacting `userId` is NOT in the members list (rare - guest or removed user), record them as `name=Unknown, email=<userId>` and note it in the summary.
5. **Write a CSV** to the current workspace directory (use the OneDrive Clawpilot folder path):
- Columns: `name,email,userId,reactedAt`
- Sort by `reactedAt` ascending.
- Default filename: `reaction-tracker-<safeEmoji>-<messageId>.csv` where `safeEmoji` is a short name like `lobster`, `heart`, `thumbsup` (ask or pick a sensible label from the emoji).
6. **Report to the user**:
- Total count of reactors matching the emoji.
- The list of names (and emails if asked).
- The saved CSV path.
- If there are reactions with OTHER emojis on the same message, mention the breakdown briefly (e.g. "Also saw 3 ❤️ and 1 👍 - not counted").
### Re-running
This skill is idempotent - running it again produces a refreshed snapshot. If the same output file exists, overwrite it (the snapshot is the latest truth). Mention to the user that they can re-run anytime to capture new reactions.
### Edge cases
- **Message not found in recent history**: page back further. If still not found after several pages, ask the user to confirm the chatId / messageId.
- **Zero reactions matching emoji**: write an empty CSV with just the header and report "No one has reacted with <emoji> yet."
- **The user hasn't posted yet**: explain that the message must exist before tracking can start, and offer to run again once they've posted.
- **Emoji normalization**: Some emojis have variation selectors (e.g. `❤️` is `❤` + U+FE0F). Match the raw `reactionType` string as returned by Graph; if no matches, try the alternate form once.
### Privacy
This is the user's own chat data. Saving the CSV locally is fine. Do NOT send the list to anyone else or post it back into the Teams chat unless the user explicitly asks.