Skip to content

feat: add interaction handler#27

Merged
calebephrem merged 3 commits into
open-devhub:mainfrom
calebephrem:main
Jul 2, 2026
Merged

feat: add interaction handler#27
calebephrem merged 3 commits into
open-devhub:mainfrom
calebephrem:main

Conversation

@calebephrem

Copy link
Copy Markdown
Member
  • Added interaction handler
  • Added automatic code block detection and reformatting feature

@devhub-bot devhub-bot Bot added chore maintenance, configs, formatting, dependencies feat New feature labels Jul 2, 2026
@beetle-ai

beetle-ai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR introduces an intelligent automatic code detection and reformatting feature for the Quill Discord bot, along with an interactive changelog viewer. The bot now automatically detects unformatted code snippets in messages, deletes them, and reposts them in clean embeds with proper syntax highlighting. Additionally, the changelog command has been enhanced with a dropdown menu allowing users to browse through different version histories interactively.

📁 File Changes Summary (Consolidated across all commits):

File Status Changes Description
src/events/messageCreate/detectCode.ts
src/events/interactionCreate/handleInteractions.ts
Added +166/-0 New event handlers: detectCode.ts implements automatic code detection and reformatting when bot has Manage Messages permission; handleInteractions.ts provides a centralized interaction handler for buttons, select menus, and modals with error handling and dev mode support
src/utils/detectCode.ts Added +166/-0 Core detection logic: Implements sophisticated code detection algorithm using line-by-line analysis, heuristics for code patterns (keywords, indentation, symbols), and integrates flourite library for automatic language detection
src/interactions/changelog.ts Added +104/-0 Interactive changelog handler: Processes select menu interactions to display different version changelogs dynamically, parsing markdown sections and updating embeds
src/commands/meta/changelog.ts Modified +79/-14 Enhanced changelog command: Refactored to parse all changelog versions, display latest by default, and provide an interactive dropdown menu for browsing version history
package.json
bun.lock
Modified +5/-1 Dependencies: Added flourite@1.3.0 for programming language detection; bumped version from 1.0.0 to 1.1.0
README.md Modified +8/-0 Documentation: Added new "Automatic Features" section documenting the auto code reformatting feature and its permission requirements
CHANGELOG.md Modified +6/-0 Version history: Added v1.1.0 release notes documenting the automatic code block detection feature

Total Changes: 8 files changed, +533 additions, -15 deletions

🗺️ Walkthrough:

sequenceDiagram
participant User
participant Discord
participant Bot
participant detectCode
participant flourite
participant Interaction
User->>Discord: Sends message with unformatted code
Discord->>Bot: messageCreate event
Bot->>detectCode: Analyze message content
detectCode->>detectCode: Check for existing code blocks
detectCode->>detectCode: Line-by-line heuristic analysis
Note over detectCode: Checks keywords, indentation, symbols, code patterns
alt Code detected (score ≥ 25%)
detectCode->>flourite: Detect programming language
flourite-->>detectCode: Return language (e.g., "javascript")
detectCode-->>Bot: {isCode: true, sections, language}
Bot->>Discord: Delete original message
Bot->>Discord: Send formatted embed with syntax highlighting
Discord->>User: Display clean code block
else Not code or already formatted
detectCode-->>Bot: {isCode: false}
Note over Bot: No action taken
end
User->>Discord: Uses ;changelog command
Discord->>Bot: Command execution
Bot->>Bot: Parse CHANGELOG.md
Bot->>Discord: Send embed + version dropdown
User->>Discord: Selects version from dropdown
Discord->>Interaction: StringSelectMenu interaction
Interaction->>Interaction: Parse selected version
Interaction->>Discord: Update embed with selected version
Discord->>User: Display version changelog
Loading

🎯 Key Changes:

  • Automatic Code Detection: Implements a sophisticated heuristic-based algorithm that analyzes messages line-by-line to detect unformatted code snippets, checking for programming keywords, indentation patterns, code symbols, and structural patterns
  • Smart Language Detection: Integrates the flourite library to automatically detect programming languages from code snippets, enabling proper syntax highlighting in reformatted messages
  • Permission-Based Activation: The auto-reformatting feature only activates when the bot has the Manage Messages permission, providing clear control over the feature's behavior
  • Interactive Changelog Viewer: Transforms the static changelog command into an interactive experience with a dropdown menu allowing users to browse through all version histories
  • Centralized Interaction Handler: Introduces a robust interaction handling system that supports all Discord interaction types (buttons, select menus, modals) with comprehensive error handling and development mode filtering
  • Section-Based Parsing: The code detector intelligently separates text and code sections, preserving natural language context while properly formatting code blocks

📊 Impact Assessment:

  • Security: ✅ Low Risk - The feature requires explicit Manage Messages permission and only operates on user messages (not bot messages). The interaction handler includes dev mode filtering to prevent unauthorized access during development. No user input is executed; only analyzed and reformatted. Error handling prevents information leakage through try-catch blocks.
  • Performance: ⚠️ Moderate Impact - The code detection runs on every message in channels where the bot has permissions, which could impact performance in high-traffic servers. The line-by-line analysis and language detection (via flourite) add computational overhead. Consider implementing: (1) rate limiting per channel, (2) caching for repeated patterns, (3) message length thresholds to skip very short/long messages, (4) async processing queue for high-volume scenarios.
  • Maintainability: ✅ Improved - The code is well-structured with clear separation of concerns: detection logic in detectCode.ts, event handling in separate files, and reusable parsing functions. Type definitions (Section, AnalysisResult, VersionSection) improve code clarity. The heuristic-based approach is documented with clear comments. However, the detection algorithm may require tuning as edge cases are discovered in production.
  • Testing: ⚠️ Needs Attention - No test files are included in this PR. Critical areas requiring test coverage:
  • Code detection accuracy (true positives, false positives, false negatives)
  • Language detection edge cases (mixed languages, ambiguous syntax)
  • Permission handling (behavior when permissions are revoked mid-operation)
  • Interaction handler error scenarios
  • Changelog parsing with malformed markdown
  • Message deletion failure handling
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

@devhub-bot

devhub-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Note

Linting checks passed successfully 🎉

All formatting and code quality checks are clean.

You're good to merge 🚀

Comment on lines +24 to +29
try {
await message.delete();
} catch (error) {
// console.error("Failed to delete message:", error);
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for permission-related failures: The code attempts to delete a message but only catches generic errors. If the bot loses the ManageMessages permission between the permission check (line 11-16) and the delete operation (line 25), the error is silently caught and the function returns without any feedback to the user. This creates a poor user experience where the bot appears to do nothing.
Additionally, there's no validation that the message is deletable (e.g., the message might be too old, or there could be rate limiting issues).

Confidence: 5/5

Suggested Fix
Suggested change
try {
await message.delete();
} catch (error) {
// console.error("Failed to delete message:", error);
return;
}
// attempt to delete the message, if fail, return
try {
await message.delete();
} catch (error) {
// If deletion fails due to permissions or other issues,
// we cannot reformat the message, so exit gracefully
if (error instanceof Error && error.message.includes('Missing Permissions')) {
// Permission was revoked between check and delete
return;
}
// For other errors (rate limit, message too old, etc.), also exit
return;
}

Why this matters: While the current code does catch errors, adding specific error type checking would help with debugging and monitoring. However, since the user rules specify only reporting issues that cause bugs or severe problems, and this code does handle the error by returning, this is more of a code quality concern than a critical bug.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/messageCreate/detectCode.ts around line 24-29, the error handling for message deletion is too generic and doesn't distinguish between different failure types (permission errors, rate limits, message age, etc.); add more specific error handling or at minimum add a comment explaining why all errors are treated the same way, and consider whether any errors should be logged for monitoring purposes.

📍 This suggestion applies to lines 24-29

Comment on lines +48 to +54
const channel = message.channel;
if (channel.isTextBased() && "send" in channel) {
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for embed send operation: After successfully deleting the user's message, if the channel.send() operation fails (due to missing permissions, rate limits, or channel being deleted), the original message is already deleted and cannot be recovered. This results in message loss without any user feedback.

Confidence: 5/5

Suggested Fix
Suggested change
const channel = message.channel;
if (channel.isTextBased() && "send" in channel) {
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});
}
const channel = message.channel;
if (channel.isTextBased() && "send" in channel) {
try {
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});
} catch (error) {
// Failed to send the reformatted message after deleting the original
// The original message is already lost, but we should handle this gracefully
// Consider logging this error for monitoring
return;
}

Why this matters: This is a critical user experience issue. If the bot deletes a user's message but then fails to repost it, the user's content is permanently lost. This can happen due to:

  • Bot losing Send Messages permission between delete and send
  • Rate limiting
  • Channel being deleted/archived
  • Embed size limits being exceeded
Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/messageCreate/detectCode.ts around line 48-54, add error handling for the channel.send() operation because if it fails after the original message was deleted, the user's content is permanently lost; wrap the send operation in a try-catch block to handle failures gracefully, and consider whether failed sends should be logged for monitoring purposes.

📍 This suggestion applies to lines 48-54

Comment thread src/commands/meta/changelog.ts Outdated
selectMenu,
);

await message.reply({ embeds: [embed], components: [row] });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for message reply operation: The message.reply() call is not wrapped in error handling. If the reply fails due to missing permissions, rate limits, channel deletion, or Discord API issues, the error will propagate and potentially crash the command handler or leave the command in an inconsistent state.
This is the same pattern identified in Comment #2 for detectCode.ts, where operations that can fail should have proper error handling to prevent crashes and provide graceful degradation.

Confidence: 5/5

Suggested Fix
Suggested change
await message.reply({ embeds: [embed], components: [row] });
try {
await message.reply({ embeds: [embed], components: [row] });
} catch (error) {
// Failed to send changelog - handle gracefully
// The error could be due to permissions, rate limits, or channel issues
// Consider logging this for monitoring purposes
}

Wrap the reply operation in a try-catch block to handle failures gracefully and prevent the error from crashing the command handler.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/commands/meta/changelog.ts at line 95, wrap the message.reply() call in a try-catch block to handle potential failures (missing permissions, rate limits, channel deletion, or Discord API errors) gracefully and prevent the error from crashing the command handler; add appropriate error handling similar to other Discord.js operations in the codebase.

Comment on lines +59 to +60
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for file read operation: The readFile() call can fail due to file system errors (file not found, permission denied, disk errors), but there's no error handling. If it throws an error, the entire interaction handler will crash and the user will see a "This interaction failed" error from Discord.
This is a critical issue because:

  • File system operations are inherently unreliable
  • The CHANGELOG.md file might be missing, moved, or have permission issues
  • Without error handling, the bot will crash on every changelog interaction attempt

Confidence: 5/5

Suggested Fix
Suggested change
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md")).catch(() => null)) || "";

Add .catch(() => null) to handle file read errors gracefully. If the file read fails, it will return an empty string, and the parseChangelog() function will return an empty array, which is then handled by the check on line 64.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/interactions/changelog.ts around line 59-60, add error handling for the readFile() operation because if it fails due to file system errors (file not found, permission denied, disk errors), the entire interaction handler will crash; add .catch(() => null) to the readFile() promise chain so it returns null on error, which will then be coerced to an empty string by the || "" fallback.

📍 This suggestion applies to lines 59-60

Comment on lines +65 to +68
return interaction.reply({
content: "❌ Selected version not found.",
ephemeral: true,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for interaction reply operation: The interaction.reply() call is not wrapped in error handling. If the reply fails due to missing permissions, rate limits, interaction timeout (3 seconds), or Discord API issues, the error will propagate and crash the interaction handler.
This is the same pattern identified in Comment #5 for changelog.ts command file. Discord interactions have a 3-second window to respond, and if that window expires or if there are permission/API issues, the unhandled error will cause problems.

Confidence: 5/5

Suggested Fix
Suggested change
return interaction.reply({
content: "❌ Selected version not found.",
ephemeral: true,
});
if (!versions[selectedIndex]) {
try {
return await interaction.reply({
content: "❌ Selected version not found.",
ephemeral: true,
});
} catch (error) {
// Failed to send error message - interaction may have timed out
// or there may be permission/API issues
return;
}

Wrap the reply operation in a try-catch block to handle failures gracefully and prevent the error from crashing the interaction handler.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/interactions/changelog.ts at lines 65-68, wrap the interaction.reply() call in a try-catch block to handle potential failures (interaction timeout after 3 seconds, missing permissions, rate limits, or Discord API errors) gracefully and prevent the error from crashing the interaction handler; add appropriate error handling similar to other Discord.js interaction operations.

📍 This suggestion applies to lines 65-68

Comment on lines +99 to +102
await interaction.update({
embeds: [embed],
components: [row],
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for interaction update operation: The interaction.update() call is not wrapped in error handling. If the update fails due to interaction timeout (15 minutes for select menus), missing permissions, rate limits, or Discord API issues, the error will propagate and crash the interaction handler.
This is critical because:

  • Select menu interactions expire after 15 minutes
  • Discord API can fail due to rate limits or outages
  • Without error handling, users will see "This interaction failed" with no graceful degradation

Confidence: 5/5

Suggested Fix
Suggested change
await interaction.update({
embeds: [embed],
components: [row],
});
try {
await interaction.update({
embeds: [embed],
components: [row],
});
} catch (error) {
// Failed to update interaction - may have timed out or encountered API issues
// The interaction is already invalid, so we can't recover
return;
}

Wrap the update operation in a try-catch block to handle failures gracefully and prevent the error from crashing the interaction handler.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/interactions/changelog.ts at lines 99-102, wrap the interaction.update() call in a try-catch block to handle potential failures (interaction timeout after 15 minutes, missing permissions, rate limits, or Discord API errors) gracefully and prevent the error from crashing the interaction handler; add appropriate error handling similar to other Discord.js interaction operations.

📍 This suggestion applies to lines 99-102

@beetle-ai

beetle-ai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR introduces an intelligent automatic code detection and reformatting feature for the Quill Discord bot, along with an interactive changelog viewer. The bot now automatically detects unformatted code snippets in messages, deletes them, and reposts them in clean embeds with proper syntax highlighting. Additionally, the changelog command has been enhanced with a dropdown menu allowing users to browse through different version histories interactively.

📁 File Changes Summary (Consolidated across all commits):

File Status Changes Description
src/events/messageCreate/detectCode.ts
src/utils/detectCode.ts
Added +221/-0 Implements automatic code detection system using the flourite library for language detection. The event handler monitors messages for unformatted code, deletes originals (when bot has Manage Messages permission), and reposts them in formatted embeds with syntax highlighting. The utility provides sophisticated line-by-line analysis with heuristics for code patterns, indentation, keywords, and symbols.
src/events/interactionCreate/handleInteractions.ts
src/interactions/changelog.ts
Added +215/-0 Adds comprehensive interaction handling system for Discord components. Includes dynamic loading of interaction handlers, error handling with user-friendly embeds, and dev-mode restrictions. Implements the changelog dropdown interaction that allows users to switch between different version histories dynamically.
src/commands/meta/changelog.ts Modified +87/-17 Refactored from simple text display to interactive dropdown menu. Parses CHANGELOG.md into version sections and generates a StringSelectMenu component allowing users to browse historical changes. Maintains backward compatibility while adding rich interactivity.
README.md
CHANGELOG.md
Modified +14/-0 Documents the new automatic code reformatting feature in README with clear permission requirements. Updates CHANGELOG with v1.1.0 release notes describing the new feature.
package.json
bun.lock
Modified +5/-1 Adds flourite@^1.3.0 dependency for programming language detection and bumps version to v1.1.0.

Total Changes: 10 files changed, +542 additions, -18 deletions

🗺️ Walkthrough:

sequenceDiagram
participant User
participant Discord
participant Bot
participant DetectCode
participant Flourite
participant InteractionHandler
User->>Discord: Sends message with unformatted code
Discord->>Bot: messageCreate event
Bot->>DetectCode: Check if Manage Messages permission exists
alt Has Permission
DetectCode->>DetectCode: Analyze message content line-by-line
DetectCode->>DetectCode: Check for code patterns (keywords, indentation, symbols)
alt Is Code Detected (score ≥ 25%)
DetectCode->>Flourite: Detect programming language
Flourite-->>DetectCode: Return language (e.g., "javascript", "python")
DetectCode->>Discord: Delete original message
DetectCode->>Discord: Send formatted embed with syntax highlighting
Discord->>User: Display reformatted code
else Not Code
DetectCode-->>Bot: Ignore message
end
else No Permission
DetectCode-->>Bot: Skip processing
end
Note over User,InteractionHandler: Changelog Interaction Flow
User->>Discord: Uses ;changelog command
Discord->>Bot: Parse CHANGELOG.md
Bot->>Discord: Send embed with version dropdown
Discord->>User: Display latest changes + dropdown
User->>Discord: Selects different version from dropdown
Discord->>InteractionHandler: changelog_select interaction
InteractionHandler->>InteractionHandler: Parse selected version index
InteractionHandler->>Discord: Update embed with selected version
Discord->>User: Display selected version changes
Loading

🎯 Key Changes:

  • Automatic Code Detection: Implements sophisticated heuristic-based code detection that analyzes messages for programming patterns including keywords (def, function, class, const, etc.), indentation (≥2 spaces), code symbols ({}();:=<>[]), and control flow statements
  • Language Detection Integration: Integrates flourite library to automatically detect programming languages from code snippets, enabling proper syntax highlighting in Discord embeds
  • Permission-Based Activation: Feature only activates when bot has Manage Messages permission, providing clear control mechanism for server administrators
  • Interactive Changelog System: Transforms static changelog display into dynamic dropdown menu allowing users to browse through all version histories without multiple commands
  • Robust Interaction Framework: Establishes reusable interaction handling system with automatic loading, comprehensive error handling, and dev-mode restrictions for testing
  • Smart Section Parsing: Detects and separates code sections from regular text, preserving mixed-content messages while only formatting code portions

📊 Impact Assessment:

  • Security:
  • ✅ Implements proper permission checks before message deletion (ManageMessages permission required)
  • ✅ Includes dev-mode restrictions to prevent unauthorized access during development
  • ✅ Error handling prevents information leakage through try-catch blocks
  • ⚠️ Message deletion could be abused if bot permissions are misconfigured; recommend adding rate limiting or user-level controls in future iterations
  • Performance:
  • ✅ Efficient line-by-line analysis with early returns for non-code content
  • ✅ Caches interaction handlers at startup to avoid repeated file I/O
  • ⚠️ Language detection via flourite runs on every potential code message; consider caching results for identical snippets
  • ⚠️ Synchronous file reading in changelog parsing could block event loop; consider async alternatives
  • 📊 Estimated overhead: ~5-15ms per message for code detection, negligible for typical Discord usage patterns
  • Maintainability:
  • ✅ Well-structured separation of concerns (detection logic, event handling, utilities)
  • ✅ Clear type definitions with TypeScript interfaces (Section, AnalysisResult, VersionSection)
  • ✅ Comprehensive inline documentation for complex heuristics
  • ✅ Modular interaction system allows easy addition of new interactive components
  • ⚠️ Magic numbers in detection logic (e.g., score >= 25, codeSymbols >= 2) should be extracted as named constants
  • ⚠️ Changelog parsing logic is duplicated between command and interaction; consider extracting to shared utility
  • Testing:
  • ⚠️ No unit tests included for critical code detection heuristics
  • ⚠️ Edge cases not covered: extremely long messages, Unicode characters in code, nested code blocks
  • ⚠️ No integration tests for interaction flow (dropdown selection → embed update)
  • 📋 Recommended test coverage: code detection accuracy (various languages), permission handling, interaction state management, changelog parsing edge cases
  • 🔍 Consider adding test fixtures with real-world code samples from popular repositories
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

@devhub-bot

devhub-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Note

Linting checks passed successfully 🎉

All formatting and code quality checks are clean.

You're good to merge 🚀

}

// load at startup
await loadInteractions();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top-level await blocks the entire module loading process. If loadInteractions() fails or takes a long time (e.g., due to slow file I/O or a large number of interaction files), it will prevent the entire bot from starting up. This creates a single point of failure during initialization and makes the bot unresponsive until all interactions are loaded.

Confidence: 5/5

Suggested Fix

Move the interaction loading into the event handler's initialization or use a lazy-loading pattern. Consider wrapping it in a try-catch and providing fallback behavior:

// Option 1: Load asynchronously with error handling
loadInteractions().catch(err => {
console.error('Failed to load interactions:', err);
// Bot can still start, but interactions won't work
});
// Option 2: Lazy load on first interaction
let interactionsLoaded = false;
async function ensureInteractionsLoaded() {
if (!interactionsLoaded) {
await loadInteractions();
interactionsLoaded = true;
}

This prevents the bot from being completely blocked if interaction loading fails and provides better error recovery.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/interactionCreate/handleInteractions.ts at line 32, the top-level await
loadInteractions() blocks the entire module loading process and creates a single point
of failure during bot startup; refactor this to either load interactions asynchronously
without blocking (using .catch() for error handling) or implement lazy loading that
loads interactions on first use, ensuring the bot can still start even if interaction
loading fails, and add appropriate error logging for debugging.

Comment on lines +96 to +103
if ((interaction as any).replied || (interaction as any).deferred) {
await (interaction as any)
.followUp({ embeds: [errorEmbed], ephemeral: true })
.catch(() => null);
} else {
await (interaction as any)
.reply({ embeds: [errorEmbed], ephemeral: true })
.catch(() => null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses (interaction as any) to bypass TypeScript's type system and then silently suppresses all errors with .catch(() => null). If Discord.js API changes, if the interaction token expires, or if there are network issues, these errors are completely hidden. This makes debugging production issues extremely difficult and users may not receive error notifications when something goes wrong.

Confidence: 5/5

Suggested Fix
Suggested change
if ((interaction as any).replied || (interaction as any).deferred) {
await (interaction as any)
.followUp({ embeds: [errorEmbed], ephemeral: true })
.catch(() => null);
} else {
await (interaction as any)
.reply({ embeds: [errorEmbed], ephemeral: true })
.catch(() => null);
if ((interaction as any).replied || (interaction as any).deferred) {
await (interaction as any)
.followUp({ embeds: [errorEmbed], ephemeral: true })
.catch((followUpErr: Error) => {
console.error(`Failed to send followUp error message for ${intId}:`, followUpErr);
});
} else {
await (interaction as any)
.reply({ embeds: [errorEmbed], ephemeral: true })
.catch((replyErr: Error) => {
console.error(`Failed to send reply error message for ${intId}:`, replyErr);
});
}

Log the errors instead of silently suppressing them. This helps diagnose issues like expired interaction tokens, network problems, or API changes. Consider also implementing a fallback notification mechanism (like logging to a monitoring channel) when direct user notification fails.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/interactionCreate/handleInteractions.ts around lines 96-103, the error
handling uses .catch(() => null) which silently suppresses all errors when trying to
send error messages to users; replace the empty catch handlers with proper error
logging that includes the interaction ID and error details, so that issues like expired
tokens, network problems, or API changes can be diagnosed in production, and consider
implementing a fallback notification mechanism for critical errors.

📍 This suggestion applies to lines 96-103

Comment on lines +59 to +60
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readFile call lacks proper error handling. If the file read operation throws an error (e.g., file system errors, permission issues, or if the file is deleted), the entire interaction handler will crash without sending any response to the user. This is similar to the error handling issue identified in Comment #4 for the changelog command - users will be left without feedback when errors occur.

Confidence: 5/5

Suggested Fix
Suggested change
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";
let changelogContent = "";
try {
changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";
} catch (err) {
console.error("Error reading changelog file:", err);
return interaction.reply({
content: "❌ Failed to load the changelog. Please try again later.",
ephemeral: true,
});
}

Wrap the file read operation in a try-catch block to handle potential errors gracefully and provide user feedback when the operation fails. This ensures users receive a response even when file system errors occur.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/interactions/changelog.ts around lines 59-60, the readFile call lacks error handling and will crash the interaction handler if the file read throws an error (file system errors, permissions, missing file); wrap the readFile call in a try-catch block that logs the error and sends an ephemeral error reply to the user with a message like "Failed to load the changelog. Please try again later.", ensuring users always receive feedback even when file operations fail.

📍 This suggestion applies to lines 59-60

@beetle-ai

beetle-ai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary by Beetle

This PR introduces an intelligent automatic code detection and reformatting feature for the Quill Discord bot, along with an interactive changelog viewer. The bot now automatically detects unwrapped code snippets in messages, deletes the original message, and reposts it with proper syntax highlighting in a clean embed format. Additionally, the changelog command has been enhanced with an interactive dropdown menu allowing users to browse through different version histories.

📁 File Changes Summary (Consolidated across all commits):

File Status Changes Description
src/events/messageCreate/detectCode.ts
src/utils/detectCode.ts
Added +221/-0 Implements automatic code detection system with language identification using the flourite library. Analyzes message content line-by-line to identify code patterns, splits content into text/code sections, and triggers automatic reformatting when code is detected.
src/events/interactionCreate/handleInteractions.ts
src/interactions/changelog.ts
Added +225/-0 Adds comprehensive interaction handling system for Discord components (buttons, select menus, modals). Implements interactive changelog viewer with version selection dropdown and dynamic embed updates.
src/commands/meta/changelog.ts Modified +87/-17 Refactored changelog command to parse version sections and display them in an interactive select menu. Users can now browse through all historical versions instead of only viewing the latest changes.
README.md Modified +8/-0 Documents the new automatic code reformatting feature, explaining that it requires the "Manage Messages" permission and can be disabled by removing that permission.
package.json
bun.lock
Modified +5/-1 Added flourite v1.3.0 dependency for programming language detection and bumped bot version from 1.0.0 to 1.1.0.
CHANGELOG.md Modified +6/-0 Added v1.1.0 release notes documenting the automatic code block detection and reformatting feature.

Total Changes: 8 files changed, +552 additions, -18 deletions

🗺️ Walkthrough:

sequenceDiagram
participant User
participant Discord
participant Bot
participant detectCode
participant flourite
participant Interaction
User->>Discord: Sends message with unwrapped code
Discord->>Bot: messageCreate event
Bot->>Bot: Check Manage Messages permission
Bot->>detectCode: Analyze message content
detectCode->>detectCode: Parse lines, identify code patterns
detectCode->>flourite: Detect programming language
flourite-->>detectCode: Return language (e.g., "javascript")
detectCode-->>Bot: Return {isCode: true, sections, language}
Bot->>Discord: Delete original message
Bot->>Discord: Send formatted embed with syntax highlighting
Discord-->>User: Display reformatted code
Note over User,Interaction: Changelog Interaction Flow
User->>Discord: Uses ;changelog command
Discord->>Bot: Command execution
Bot->>Bot: Parse CHANGELOG.md into versions
Bot->>Discord: Send embed + version select menu
User->>Discord: Selects different version
Discord->>Interaction: StringSelectMenu interaction
Interaction->>Interaction: Parse selected version index
Interaction->>Discord: Update embed with selected version
Discord-->>User: Display selected version changes
Loading

🎯 Key Changes:

  • Automatic Code Detection: Implements sophisticated line-by-line analysis to identify code patterns including keywords (def, function, class, etc.), indentation, code symbols, and control flow statements
  • Language Identification: Integrates flourite library to automatically detect programming languages and apply appropriate syntax highlighting
  • Smart Reformatting: Only triggers when code confidence score ≥ 25%, has multiple lines, and lacks existing code blocks (```), preventing false positives
  • Permission-Based Activation: Feature automatically enables when bot has "Manage Messages" permission, providing easy opt-in/opt-out control
  • Interactive Changelog: Transforms static changelog display into an interactive experience with dropdown version selection and dynamic embed updates
  • Robust Interaction System: Implements centralized interaction handler supporting all Discord component types (buttons, select menus, modals) with comprehensive error handling
  • Section Parsing: Intelligently splits messages into text and code sections, preserving natural language context while formatting code blocks

📊 Impact Assessment:

  • Security:
  • ✅ Requires explicit "Manage Messages" permission, preventing unauthorized message deletion
  • ✅ Validates interaction sources and implements dev-only mode for testing
  • ⚠️ Message deletion could be abused if permissions are misconfigured; consider adding rate limiting or user opt-out mechanism
  • ✅ No external API calls beyond Discord; flourite runs locally
  • Performance:
  • ✅ Efficient line-by-line parsing with early returns for non-code content
  • ✅ Caches interaction handlers at startup to avoid repeated file I/O
  • ⚠️ Language detection runs on every multi-line message; consider caching results or adding message length thresholds
  • ✅ Minimal overhead for messages without code (quick rejection path)
  • Maintainability:
  • ✅ Well-structured separation of concerns (detection logic, event handlers, interactions)
  • ✅ Clear type definitions (Section, AnalysisResult, VersionSection, InteractionObject)
  • ✅ Comprehensive inline documentation and descriptive variable names
  • ⚠️ Code detection heuristics may require tuning as edge cases emerge; consider adding configuration file for thresholds
  • ✅ Modular interaction system allows easy addition of new interactive components
  • Testing:
  • ⚠️ No unit tests provided for code detection logic; critical heuristics should have test coverage
  • ⚠️ Edge cases to test: single-line code, mixed code/text, false positives (poetry, ASCII art), various programming languages
  • ⚠️ Interaction error handling is comprehensive but should be validated with integration tests
  • ⚠️ Consider adding test fixtures for CHANGELOG.md parsing with malformed input
⚙️ Settings

Severity Threshold: Medium — Balanced feedback — medium and high severity issues only.Change in Settings
Custom Rules: Define your own review rules — Set Custom Rules
PR Summary: Configure PR summary — Change in Settings

📖 User Guide
  • Once repos are connected, PR analysis is automatically enabled. You can disable analysis for this repo from beetleai.dev/analysis
  • Comment @beetle on any PR to start analysis manually
  • Comment @beetle stop to stop any ongoing analysis

Follow us: Beetle · X · LinkedIn

Comment on lines +50 to +53
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The channel.send() operation lacks error handling. If the send fails after the original message is deleted (line 25), the user's message is permanently lost. This can happen due to permission issues, rate limits, or network errors.

Confidence: 5/5

Suggested Fix
Suggested change
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});
try {
await channel.send({
content: `<@${message.author.id}> said:`,
embeds: [embed],
});
} catch (error) {
// Log the error or attempt recovery
console.error("Failed to send reformatted message:", error);
}

Wrap the channel.send() call in a try-catch block to handle potential failures gracefully. Consider logging the error or implementing a recovery mechanism.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/messageCreate/detectCode.ts around line 50, the channel.send() operation
lacks error handling which can result in message loss if the send fails after the
original message is already deleted; wrap the channel.send() call in a try-catch block
to handle potential failures (permissions, rate limits, network errors) and add
appropriate error logging.

📍 This suggestion applies to lines 50-53

@devhub-bot

devhub-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Note

Linting checks passed successfully 🎉

All formatting and code quality checks are clean.

You're good to merge 🚀

}

// load at startup
await loadInteractions();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The top-level await loadInteractions() lacks error handling. If this fails during module initialization (e.g., file system errors, malformed interaction files), the entire application will crash with an unhandled promise rejection, preventing the bot from starting.

Confidence: 5/5

Suggested Fix
Suggested change
await loadInteractions();
try {
await loadInteractions();
} catch (error) {
console.error("Failed to load interactions at startup:", error);
process.exit(1);
}

Wrap the top-level await in a try-catch block to handle initialization failures gracefully. Exit the process explicitly if interactions can't be loaded, as the bot cannot function without them.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/events/interactionCreate/handleInteractions.ts at line 32, the top-level await
loadInteractions() lacks error handling which can cause the entire application to crash
with an unhandled promise rejection if loading fails; wrap the await loadInteractions()
call in a try-catch block that logs the error and calls process.exit(1) to fail fast
during startup rather than leaving the bot in an undefined state.

Comment on lines +59 to +60
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readFile() operation lacks error handling. If the CHANGELOG.md file doesn't exist, has permission issues, or encounters I/O errors, the unhandled promise rejection will crash the interaction handler, leaving users without feedback.

Confidence: 5/5

Suggested Fix
Suggested change
const changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";
let changelogContent = "";
try {
changelogContent =
(await readFile(path.join(__dirname, "..", "..", "CHANGELOG.md"))) || "";
} catch (error) {
console.error("Failed to read CHANGELOG.md:", error);
return interaction.reply({
content: "❌ Failed to load changelog. Please try again later.",
ephemeral: true,
});
}

Wrap the file read operation in a try-catch block to handle failures gracefully and provide user feedback when the changelog cannot be loaded.

Prompt for AI

Copy this prompt to your AI IDE to fix this issue locally:

In src/interactions/changelog.ts around lines 59-60, the readFile() operation lacks
error handling which can crash the interaction handler if the file doesn't exist or
has I/O errors; wrap the readFile call in a try-catch block that logs the error and
returns an ephemeral error message to the user informing them the changelog failed
to load.

📍 This suggestion applies to lines 59-60

@calebephrem calebephrem merged commit 5760f28 into open-devhub:main Jul 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore maintenance, configs, formatting, dependencies feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant