"""Read reactions on a specific Discord message."""
from __future__ import annotations
import json
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from tool_context import ToolContext
logger = logging.getLogger(__name__)
_REACTION_USERS_PREVIEW_LIMIT = 50
TOOL_NAME = "get_message_reactions"
TOOL_DESCRIPTION = (
"Retrieve reactions on a specific Discord message, including sample "
"users per emoji (up to a cap—see users_truncated). "
"Pass channel_id when known for instant lookup; omitting it tries the "
"current channel, then guild, then a full scan (slow)."
)
TOOL_PARAMETERS = {
"type": "object",
"properties": {
"message_id": {
"type": "string",
"description": "The ID of the message to check reactions on.",
},
"channel_id": {
"type": "string",
"description": (
"Channel containing the message. Strongly recommended for "
"fast lookup; if omitted, context channel → same guild → "
"global search."
),
},
},
"required": ["message_id"],
}
[docs]
async def run(
message_id: str,
channel_id: str | None = None,
ctx: ToolContext | None = None,
) -> str:
"""Fetch reactions on a message and return them as JSON.
Args:
message_id: The Discord message ID.
channel_id: Optional channel ID to narrow the search.
ctx: Tool execution context.
Returns:
JSON string mapping emoji → list of user display names.
"""
from tools._discord_helpers import (
require_discord_client,
resolve_discord_message,
)
client = require_discord_client(ctx)
if isinstance(client, str):
return client
try:
msg_id = int(message_id)
except ValueError:
return f"Error: Invalid message ID: '{message_id}'."
msg = await resolve_discord_message(
client,
msg_id,
message_id_display=message_id,
channel_id=channel_id,
ctx=ctx,
)
if isinstance(msg, str):
return msg
if not msg.reactions:
return json.dumps({
"message_id": message_id,
"reactions": [],
"summary": "No reactions on this message.",
})
reactions_data = []
for reaction in msg.reactions:
emoji_str = str(reaction.emoji)
users: list[str] = []
truncated = False
try:
async for user in reaction.users(
limit=_REACTION_USERS_PREVIEW_LIMIT,
):
users.append(user.display_name)
truncated = reaction.count > len(users)
except Exception:
users = [f"({reaction.count} users – could not fetch names)"]
truncated = False
reactions_data.append({
"emoji": emoji_str,
"count": reaction.count,
"users": users,
"users_truncated": truncated,
})
return json.dumps({
"message_id": message_id,
"channel_id": str(msg.channel.id),
"total_reactions": sum(r["count"] for r in reactions_data),
"reactions": reactions_data,
"users_preview_limit": _REACTION_USERS_PREVIEW_LIMIT,
}, indent=2)