feat: add free-will message reaction#28
Conversation
Summary by BeetleThis PR modernizes the bot's runtime environment by migrating from Node.js to Bun, introduces an autonomous emoji reaction system that allows the AI to react to messages based on sentiment analysis, and applies comprehensive code formatting with Prettier across the entire codebase. 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 25 files changed, +587 additions, -145 deletions 🗺️ Walkthrough:sequenceDiagram
participant User
participant Discord
participant Bot
participant AI Model
participant React Tool
User->>Discord: Sends message mentioning bot
Discord->>Bot: messageCreate event
Bot->>Bot: Parse message & build context
Note over Bot: Includes channelId, messageId, interaction type
Bot->>AI Model: Generate response with tools (search, react, stock, getImage)
AI Model->>AI Model: Analyze message sentiment
Note over AI Model: Funny? Sad? Technical? Frustrating? Wholesome?
alt Message warrants reaction
AI Model->>React Tool: Call react tool with emoji
React Tool->>Discord: Fetch channel & message
React Tool->>Discord: Add emoji reaction
React Tool-->>AI Model: Success confirmation
end
AI Model-->>Bot: Text response + tool results
Bot->>Discord: Send reply to user
Discord->>User: Display response with reaction
🎯 Key Changes:
📊 Impact Assessment:
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
| // Inject user interaction properties so the model can freely match reactions to message tone | ||
| if (currentContext) { | ||
| sections.push( | ||
| `[Discord Direct Conversation Context]\n` + | ||
| `- Channel ID: ${currentContext.channelId}\n` + | ||
| `- Target Message ID: ${currentContext.messageId}\n` + | ||
| `- Interaction Hook: ${currentContext.interactionType}\n\n` + | ||
| `[Autonomous Reactions Instructions]:\n` + | ||
| `You are explicitly authorized to use the 'react' tool autonomously on the incoming user message if its tone, content, or context warrants an emotional reaction. Read the sentiment carefully:\n` + | ||
| `- If the message is genuinely funny, humorous, or witty → React with '😂', '🤣', or '💀'.\n` + | ||
| `- If the message contains obvious flame bait, trolling, or friendly sarcasm → React with '🤨', '🤡', or '😡'.\n` + | ||
| `- If the message shows hype, an achievement, or excellent news → React with '🔥', '🚀', or '🙌'.\n` + | ||
| `- If the message is sad, unfortunate, moving, or a "feels bad man" moment → React with '😭', '🥺', or '💔'.\n` + | ||
| `- If the message is highly technical, detailed, a deep-dive, or full of "nerd" energy → React with '🤓', '🧠', or '📝'.\n` + | ||
| `- If the message is completely bizarre, confusing, or leaves you speechless → React with '🤔', '❓', or '🫠'.\n` + | ||
| `- If the message mentions a catastrophic bug, a production crash, or scary code → React with '😱', '😨', or '💥'.\n` + | ||
| `- If the message is wholesome, genuinely kind, or expresses warm appreciation → React with '❤️', '🥰', or '✨'.\n` + | ||
| `- If the message expresses sheer frustration, annoying blockers, or unhelpful errors → React with '😤', '🤬', or '💢'.\n` + | ||
| `- If the message talks about being burnt out, working late hours, or being completely exhausted → React with '😴', '😮💨', or '🥱'.\n` + | ||
| `- If the user shares something highly unexpected, wild gossip, or mind-blowing tech news → React with '🤯', '😲', or '👁️🗨️'.\n` + | ||
| `- If it's a routine query or structured setup, you can skip the tool call entirely.\n` + | ||
| `Execute the 'react' tool dynamically before finalizing your text response when applicable.`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
The autonomous reactions system prompt is extremely verbose (24+ lines of detailed emoji mapping instructions). This has several problems:
- Token Cost: This massive prompt will be sent with every AI request, consuming significant tokens and increasing API costs
- Maintainability: Hardcoded emoji mappings in a giant template literal are difficult to maintain and update
- Prompt Injection Risk: The extensive instructions about autonomous behavior could be manipulated by carefully crafted user messages
- Performance: The AI model must process this entire instruction set on every request, even for simple queries
Confidence: 5/5
Suggested Fix
Consider refactoring this to:
- Extract emoji mappings to a configuration object
- Generate the prompt dynamically from the config
- Make the instructions more concise (e.g., "React with appropriate emojis based on message sentiment")
- Consider moving detailed mappings to a separate training/fine-tuning approach rather than prompt engineering
Example approach:
const REACTION_GUIDE = {
funny: ['😂', '🤣', '💀'],
trolling: ['🤨', '🤡', '😡'],
// ... other categories
};
// Then generate a much shorter prompt:
const reactionPrompt = `You may use the 'react' tool to add emoji reactions based on message sentiment. Available categories: ${Object.keys(REACTION_GUIDE).join(', ')}`;Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/commands/ai/askai.js around lines 512-535, refactor the extremely verbose
autonomous reactions system prompt to reduce token costs and improve maintainability.
Extract the emoji mappings to a configuration object, generate a concise prompt
dynamically, and consider whether all these detailed instructions need to be in
every API call or could be simplified to "React with appropriate emojis based on
message sentiment and tone."
📍 This suggestion applies to lines 512-535
| ); | ||
|
|
||
| const isReplyToBot = message.reference?.messageId ? true : false; | ||
| const mentionsBot = message.mentions.has(client.user); |
There was a problem hiding this comment.
The code assumes client.user is always defined, but if the client hasn't fully initialized or there's a connection issue, this could throw a runtime error. This would crash the command handler.
Confidence: 4/5
Suggested Fix
| const mentionsBot = message.mentions.has(client.user); | |
| const mentionsBot = client.user ? message.mentions.has(client.user) : false; | |
Add a null check to prevent potential crashes if client.user is undefined during edge cases like bot startup or reconnection scenarios.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/commands/ai/askai.js around line 139, add a null check for client.user
before calling message.mentions.has(client.user) to prevent potential runtime
errors during bot initialization or connection issues. Use:
`const mentionsBot = client.user ? message.mentions.has(client.user) : false;`
| const channel = await client.channels.fetch(channelId); | ||
|
|
||
| if (!channel || !channel.isTextBased()) { | ||
| return { | ||
| success: false, | ||
| error: "Channel not found or is not a text-based channel.", | ||
| }; | ||
| } | ||
|
|
||
| // 2. Fetch the specific message | ||
| const discordMessage = await channel.messages.fetch(messageId); | ||
|
|
||
| if (!discordMessage) { | ||
| return { success: false, error: "Message not found." }; | ||
| } | ||
|
|
||
| // 3. React to the message | ||
| await discordMessage.react(emoji); |
There was a problem hiding this comment.
Missing permission checks before attempting to react to messages. If the bot lacks ADD_REACTIONS permission in the channel, the react() call will throw an error. This could cause the tool to fail unexpectedly and return a generic error message instead of a clear permission error.
Confidence: 5/5
Suggested Fix
Add permission checks before attempting to fetch and react to messages:
| const channel = await client.channels.fetch(channelId); | |
| if (!channel || !channel.isTextBased()) { | |
| return { | |
| success: false, | |
| error: "Channel not found or is not a text-based channel.", | |
| }; | |
| } | |
| // 2. Fetch the specific message | |
| const discordMessage = await channel.messages.fetch(messageId); | |
| if (!discordMessage) { | |
| return { success: false, error: "Message not found." }; | |
| } | |
| // 3. React to the message | |
| await discordMessage.react(emoji); | |
| // 1. Fetch the channel from the Discord client cache or API | |
| const channel = await client.channels.fetch(channelId); | |
| if (!channel || !channel.isTextBased()) { | |
| return { | |
| success: false, | |
| error: "Channel not found or is not a text-based channel.", | |
| }; | |
| } | |
| // Check if bot has permission to add reactions | |
| if (!channel.permissionsFor(client.user)?.has('AddReactions')) { | |
| return { | |
| success: false, | |
| error: "Bot does not have permission to add reactions in this channel.", | |
| }; | |
| } | |
| // 2. Fetch the specific message | |
| const discordMessage = await channel.messages.fetch(messageId); | |
| if (!discordMessage) { | |
| return { success: false, error: "Message not found." }; | |
| } | |
| // 3. React to the message | |
| await discordMessage.react(emoji); | |
This prevents runtime errors and provides clear feedback when permissions are missing.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/tools/react.js around lines 24-41, add a permission check after fetching
the channel to verify the bot has 'AddReactions' permission before attempting to
react to messages. Use `channel.permissionsFor(client.user)?.has('AddReactions')`
and return a clear error message if the permission is missing, preventing runtime
errors and providing better user feedback.
📍 This suggestion applies to lines 24-41
No description provided.