Skip to content

Conversation

@dm94
Copy link
Owner

@dm94 dm94 commented Aug 11, 2025

  • Add retry mechanism for failed message sends with max attempts
  • Implement proper state management for failed messages
  • Refactor message processing into separate methods
  • Add input validation and length checks
  • Improve error handling and logging
  • Fix incorrect variable assignment for lastOtherSize

Summary by Sourcery

Improve external chat message handling and error resilience by adding retry logic, input validation, enhanced error handling and logging, and refactoring message and chat processing code.

New Features:

  • Add retry mechanism with a maximum of 3 attempts and state tracking for failed message sends
  • Implement input validation and enforce a 255-character limit on outgoing messages

Bug Fixes:

  • Fix incorrect assignment of lastOtherSize when initializing chat processors
  • Handle exceptions in GUI open/close methods to prevent crashes

Enhancements:

  • Refactor message queuing and dispatch into dedicated methods for clearer processing and retry handling
  • Split chat processor updates into separate methods for global and other chats
  • Enhance sendMessage logic with GUI focus checks, input clearing via key events, timing delays, and error logging

- Add retry mechanism for failed message sends with max attempts
- Implement proper state management for failed messages
- Refactor message processing into separate methods
- Add input validation and length checks
- Improve error handling and logging
- Fix incorrect variable assignment for lastOtherSize
@dm94 dm94 self-assigned this Aug 11, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Aug 11, 2025

Reviewer's Guide

Introduces a resilient message dispatch pipeline by extracting core processing steps into dedicated methods, adding retry logic with capped attempts, input validation, and consistent error handling and logging.

Sequence diagram for message sending with retry mechanism

sequenceDiagram
    participant ExternalChat
    participant ChatGUI
    participant API
    participant ExtensionsAPI
    ExternalChat->>ChatGUI: Ensure GUI is open
    alt GUI open fails
        ExternalChat->>ExtensionsAPI: Log warning (Failed to open chat GUI)
        ExternalChat-->>ExternalChat: Abort message send
    else GUI open succeeds
        ExternalChat->>API: Send message
        alt Message send succeeds
            ExternalChat-->>ExternalChat: Reset retry state
        else Message send fails
            ExternalChat-->>ExternalChat: Update retry count
            alt Retry count >= MAX_RETRY_ATTEMPTS
                ExternalChat->>ExtensionsAPI: Log warning (Failed to send after max attempts)
                ExternalChat-->>ExternalChat: Remove failed message
            else Retry count < MAX_RETRY_ATTEMPTS
                ExternalChat-->>ExternalChat: Will retry next tick
            end
        end
    end
Loading

Class diagram for updated ExternalChat message handling

classDiagram
    class ExternalChat {
        - LinkedList<String> pendingMessages
        - int messageRetryCount
        - static int MAX_RETRY_ATTEMPTS
        - String lastFailedMessage
        + addMessageToPendingList()
        + onTickTask()
        + processPendingMessages()
        + handleMessageSending(String)
        + handleSuccessfulMessageSend()
        + handleFailedMessageSend(String)
        + updateRetryCount(String)
        + removeFailedMessageAndLog(String)
        + resetRetryState()
        + updateChatProcessors()
        + updateGlobalChatProcessor()
        + updateOtherChatProcessor()
        + sendMessage(String): boolean
    }
    ExternalChat --> ChatProcessor
    ExternalChat --> ExtensionsAPI
    ExternalChat --> PluginAPI
Loading

File-Level Changes

Change Details Files
Add retry mechanism for message sends
  • Introduce messageRetryCount, MAX_RETRY_ATTEMPTS and lastFailedMessage
  • Track and update retry count per message
  • Remove and log messages after reaching max retry attempts
  • Reset retry state on successful send
src/main/java/com/deeme/tasks/externalchat/ExternalChat.java
Refactor message dispatch into dedicated methods
  • Extract processPendingMessages and integrate into onTickTask
  • Introduce handleMessageSending with success/failure handlers
  • Decompose updateChatProcessors into updateGlobalChatProcessor and updateOtherChatProcessor
  • Encapsulate GUI open/close logic into separate methods
src/main/java/com/deeme/tasks/externalchat/ExternalChat.java
Add input validation and length checks
  • Use Optional to validate and trim input before queuing
  • Enforce non-empty and max 255 character limit
  • Guard sendMessage against null, empty or invalid GUI dimensions
src/main/java/com/deeme/tasks/externalchat/ExternalChat.java
Enhance error handling and logging
  • Wrap GUI open/close and sendMessage operations in try/catch blocks
  • Replace hard-coded feature name with EXTERNAL_CHAT_NAME constant
  • Log warnings via extensionsAPI on failures
src/main/java/com/deeme/tasks/externalchat/ExternalChat.java
Fix incorrect lastOtherSize assignment
  • Correct mistakenly duplicated lastGlobalSize assignment to set lastOtherSize in showChat
src/main/java/com/deeme/tasks/externalchat/ExternalChat.java

Possibly linked issues

  • #123: PR adds message retry and improves error handling, directly resolving the issue's failed message sending bug.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @dm94 - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/main/java/com/deeme/tasks/externalchat/ExternalChat.java:191` </location>
<code_context>

-        closeGui();
+    private void updateRetryCount(String message) {
+        if (lastFailedMessage == null || !lastFailedMessage.equals(message)) {
+            lastFailedMessage = message;
+            messageRetryCount = 1;
+        } else {
+            messageRetryCount++;
</code_context>

<issue_to_address>
Retry count logic may not handle rapid message changes correctly.

Rapid changes to the pending message could cause the retry count to reset unintentionally. Please verify if this aligns with the desired retry behavior.
</issue_to_address>

### Comment 2
<location> `src/main/java/com/deeme/tasks/externalchat/ExternalChat.java:201` </location>
<code_context>
+
+    private void removeFailedMessageAndLog(String message) {
+        pendingMessages.pollFirst();
+        extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
+                "Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + message);
+        resetRetryState();
+    }
</code_context>

<issue_to_address>
Logging failed messages may expose sensitive user data.

Evaluate if logging the entire message is necessary. If sensitive data may be present, consider masking or truncating the logged content.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    private void removeFailedMessageAndLog(String message) {
        pendingMessages.pollFirst();
        extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
                "Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + message);
        resetRetryState();
    }
=======
    private void removeFailedMessageAndLog(String message) {
        pendingMessages.pollFirst();
        String safeMessage;
        if (message != null && message.length() > 30) {
            safeMessage = message.substring(0, 30) + "...";
        } else {
            safeMessage = message;
        }
        extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
                "Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + safeMessage);
        resetRetryState();
    }
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `src/main/java/com/deeme/tasks/externalchat/ExternalChat.java:311` </location>
<code_context>
-        int yPoint = (int) chatGui.getHeight() - INPUT_BOTTOM_OFFSET - (rnd.nextInt(INPUT_HEIGHT));
-        chatGui.click(xPoint, yPoint);
+    private boolean sendMessage(String message) {
+        if (message == null || message.trim().isEmpty()) {
+            return false;
+        }
+
</code_context>

<issue_to_address>
Early return for empty messages is good, but consider logging or user feedback.

Returning false without any indication may hinder debugging. Logging or user feedback would help identify unexpected empty messages.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        if (message == null || message.trim().isEmpty()) {
            return false;
        }
=======
        if (message == null || message.trim().isEmpty()) {
            System.out.println("[ExternalChat] Attempted to send empty or null message. Ignoring.");
            // Alternatively, use a logger if available, e.g.:
            // logger.warn("Attempted to send empty or null message. Ignoring.");
            return false;
        }
>>>>>>> REPLACE

</suggested_fix>

### Comment 4
<location> `src/main/java/com/deeme/tasks/externalchat/ExternalChat.java:331` </location>
<code_context>
+            Time.sleep(200);
+
+            // Clear any existing text in input field
+            API.keyClick(1); // Ctrl+A
+            Time.sleep(50);

-        Time.sleep(100);
</code_context>

<issue_to_address>
Key code for Ctrl+A may not be portable or clear.

Consider replacing the hardcoded value with a named constant or utility method to improve portability and code clarity.

Suggested implementation:

```java
            // Clear any existing text in input field
            API.keyClick(KEY_CTRL_A); // Ctrl+A
            Time.sleep(50);

```

```java
            // Press Enter to send
            API.keyClick(10); // Enter key

```

```java
    // Add this constant at the top of the class (with other constants)
    private static final int KEY_CTRL_A = 1; // Key code for Ctrl+A (select all)

```

If your codebase has a utility method for key combinations (e.g., `API.keyCombo(int key1, int key2)`), consider using that instead of a single key code. Adjust the constant name and value if your key codes differ or are imported from a library.
</issue_to_address>

### Comment 5
<location> `src/main/java/com/deeme/tasks/externalchat/ExternalChat.java:339` </location>
<code_context>
+            Time.sleep(100);
+
+            // Press Enter to send
+            API.keyClick(10); // Enter key
+
+            // Additional delay to ensure message is sent
+            Time.sleep(300);
+
</code_context>

<issue_to_address>
Hardcoded key codes for Enter may reduce maintainability.

Consider replacing the hardcoded value with a named constant or enum for better clarity and portability.

Suggested implementation:

```java
            // Press Enter to send
            API.keyClick(ENTER_KEY_CODE); // Enter key

```

```java
    private static final int INPUT_WIDTH_OFFSET = 20;
    private static final int INPUT_BOTTOM_OFFSET = 15;
    private static final int ENTER_KEY_CODE = 10;

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +191 to +193
if (lastFailedMessage == null || !lastFailedMessage.equals(message)) {
lastFailedMessage = message;
messageRetryCount = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Retry count logic may not handle rapid message changes correctly.

Rapid changes to the pending message could cause the retry count to reset unintentionally. Please verify if this aligns with the desired retry behavior.

Comment on lines +199 to +204
private void removeFailedMessageAndLog(String message) {
pendingMessages.pollFirst();
extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
"Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + message);
resetRetryState();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🚨 suggestion (security): Logging failed messages may expose sensitive user data.

Evaluate if logging the entire message is necessary. If sensitive data may be present, consider masking or truncating the logged content.

Suggested change
private void removeFailedMessageAndLog(String message) {
pendingMessages.pollFirst();
extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
"Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + message);
resetRetryState();
}
private void removeFailedMessageAndLog(String message) {
pendingMessages.pollFirst();
String safeMessage;
if (message != null && message.length() > 30) {
safeMessage = message.substring(0, 30) + "...";
} else {
safeMessage = message;
}
extensionsAPI.getFeatureInfo(this.getClass()).addWarning(EXTERNAL_CHAT_NAME,
"Failed to send message after " + MAX_RETRY_ATTEMPTS + " attempts: " + safeMessage);
resetRetryState();
}

Comment on lines +311 to +313
if (message == null || message.trim().isEmpty()) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Early return for empty messages is good, but consider logging or user feedback.

Returning false without any indication may hinder debugging. Logging or user feedback would help identify unexpected empty messages.

Suggested change
if (message == null || message.trim().isEmpty()) {
return false;
}
if (message == null || message.trim().isEmpty()) {
System.out.println("[ExternalChat] Attempted to send empty or null message. Ignoring.");
// Alternatively, use a logger if available, e.g.:
// logger.warn("Attempted to send empty or null message. Ignoring.");
return false;
}

Comment on lines +331 to +332
API.keyClick(1); // Ctrl+A
Time.sleep(50);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Key code for Ctrl+A may not be portable or clear.

Consider replacing the hardcoded value with a named constant or utility method to improve portability and code clarity.

Suggested implementation:

            // Clear any existing text in input field
            API.keyClick(KEY_CTRL_A); // Ctrl+A
            Time.sleep(50);
            // Press Enter to send
            API.keyClick(10); // Enter key
    // Add this constant at the top of the class (with other constants)
    private static final int KEY_CTRL_A = 1; // Key code for Ctrl+A (select all)

If your codebase has a utility method for key combinations (e.g., API.keyCombo(int key1, int key2)), consider using that instead of a single key code. Adjust the constant name and value if your key codes differ or are imported from a library.

Comment on lines +339 to +341
API.keyClick(10); // Enter key

// Additional delay to ensure message is sent
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Hardcoded key codes for Enter may reduce maintainability.

Consider replacing the hardcoded value with a named constant or enum for better clarity and portability.

Suggested implementation:

            // Press Enter to send
            API.keyClick(ENTER_KEY_CODE); // Enter key
    private static final int INPUT_WIDTH_OFFSET = 20;
    private static final int INPUT_BOTTOM_OFFSET = 15;
    private static final int ENTER_KEY_CODE = 10;

@dm94 dm94 marked this pull request as draft August 11, 2025 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants