diff --git a/csharp/Platform.Bot/Program.cs b/csharp/Platform.Bot/Program.cs index 521a6b95..81ea3254 100644 --- a/csharp/Platform.Bot/Program.cs +++ b/csharp/Platform.Bot/Program.cs @@ -95,7 +95,7 @@ private static async Task Main(string[] args) var dbContext = new FileStorage(databaseFilePath?.FullName ?? new TemporaryFile().Filename); Console.WriteLine($"Bot has been started. {Environment.NewLine}Press CTRL+C to close"); var githubStorage = new GitHubStorage(githubUserName, githubApiToken, githubApplicationName); - var issueTracker = new IssueTracker(githubStorage, new HelloWorldTrigger(githubStorage, dbContext, fileSetName), new OrganizationLastMonthActivityTrigger(githubStorage), new LastCommitActivityTrigger(githubStorage), new AdminAuthorIssueTriggerDecorator(new ProtectDefaultBranchTrigger(githubStorage), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationRepositoriesDefaultBranchTrigger(githubStorage, dbContext), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationPullRequestsBaseBranchTrigger(githubStorage, dbContext), githubStorage)); + var issueTracker = new IssueTracker(githubStorage, new HelloWorldTrigger(githubStorage, dbContext, fileSetName), new OrganizationLastMonthActivityTrigger(githubStorage), new LastCommitActivityTrigger(githubStorage), new AdminAuthorIssueTriggerDecorator(new ProtectDefaultBranchTrigger(githubStorage), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationRepositoriesDefaultBranchTrigger(githubStorage, dbContext), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationPullRequestsBaseBranchTrigger(githubStorage, dbContext), githubStorage), new DeletePlusMessagesTrigger(githubStorage)); var pullRequenstTracker = new PullRequestTracker(githubStorage, new MergeDependabotBumpsTrigger(githubStorage)); var timestampTracker = new DateTimeTracker(githubStorage, new CreateAndSaveOrganizationRepositoriesMigrationTrigger(githubStorage, dbContext, Path.Combine(Directory.GetCurrentDirectory(), "/github-migrations"))); var cancellation = new CancellationTokenSource(); diff --git a/csharp/Platform.Bot/Triggers/DeletePlusMessagesTrigger.cs b/csharp/Platform.Bot/Triggers/DeletePlusMessagesTrigger.cs new file mode 100644 index 00000000..48f913d3 --- /dev/null +++ b/csharp/Platform.Bot/Triggers/DeletePlusMessagesTrigger.cs @@ -0,0 +1,112 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Interfaces; +using Octokit; +using Storage.Remote.GitHub; + +namespace Platform.Bot.Triggers +{ + using TContext = Issue; + + /// + /// + /// Represents a trigger that deletes messages with "+ " as reply to other messages + /// after 6 hours have passed since the last "+ " message. + /// + /// + /// + /// + internal class DeletePlusMessagesTrigger : ITrigger + { + private readonly GitHubStorage _storage; + private readonly TimeSpan _deletionThreshold = TimeSpan.FromHours(6); + + /// + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// The GitHub storage instance. + /// + /// + public DeletePlusMessagesTrigger(GitHubStorage storage) + { + _storage = storage; + } + + /// + /// + /// Determines whether this trigger should process the given issue. + /// + /// + /// + /// + /// The issue context. + /// + /// + /// + /// True if the issue should be processed, false otherwise. + /// + /// + public async Task Condition(TContext context) + { + try + { + var comments = await _storage.GetIssueComments(context.Repository.Id, context.Number); + var plusComments = comments.Where(c => c.Body.Trim() == "+ ").ToList(); + + if (!plusComments.Any()) + { + return false; + } + + var lastPlusComment = plusComments.OrderByDescending(c => c.CreatedAt).First(); + var timeSinceLastPlus = DateTimeOffset.UtcNow - lastPlusComment.CreatedAt; + + return timeSinceLastPlus >= _deletionThreshold; + } + catch (Exception ex) + { + Console.WriteLine($"Error checking condition for issue {context.Number}: {ex.Message}"); + return false; + } + } + + /// + /// + /// Deletes all "+ " messages from the issue if 6 hours have passed since the last one. + /// + /// + /// + /// + /// The issue context. + /// + /// + public async Task Action(TContext context) + { + try + { + var comments = await _storage.GetIssueComments(context.Repository.Id, context.Number); + var plusComments = comments.Where(c => c.Body.Trim() == "+ ").ToList(); + + foreach (var comment in plusComments) + { + await _storage.DeleteIssueComment(context.Repository.Id, comment.Id); + Console.WriteLine($"Deleted '+ ' comment {comment.Id} from issue {context.Number} in repository {context.Repository.FullName}"); + } + + if (plusComments.Any()) + { + Console.WriteLine($"Deleted {plusComments.Count} '+ ' comment(s) from issue {context.Number} in repository {context.Repository.FullName}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error deleting '+ ' comments from issue {context.Number}: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/csharp/Storage/RemoteStorage/GitHubStorage.cs b/csharp/Storage/RemoteStorage/GitHubStorage.cs index 888a7426..409d89ef 100644 --- a/csharp/Storage/RemoteStorage/GitHubStorage.cs +++ b/csharp/Storage/RemoteStorage/GitHubStorage.cs @@ -307,6 +307,16 @@ public Task CreateIssueComment(long repositoryId, int issueNumber, return Client.Issue.Comment.Create(repositoryId, issueNumber, message); } + public Task> GetIssueComments(long repositoryId, int issueNumber) + { + return Client.Issue.Comment.GetAllForIssue(repositoryId, issueNumber); + } + + public Task DeleteIssueComment(long repositoryId, long commentId) + { + return Client.Issue.Comment.Delete(repositoryId, (int)commentId); + } + #endregion #region Branch diff --git a/examples/test_plus_message_deletion.md b/examples/test_plus_message_deletion.md new file mode 100644 index 00000000..b9cf854d --- /dev/null +++ b/examples/test_plus_message_deletion.md @@ -0,0 +1,47 @@ +# Test Plan for Plus Message Deletion Feature + +## Overview +This document outlines how to test the new "+ " message deletion feature. + +## Feature Description +The bot now automatically deletes comments containing only "+ " (plus space) from GitHub issues after 6 hours have passed since the last such comment was posted. + +## Implementation Details +1. **GitHubStorage.cs**: Added methods to get and delete issue comments + - `GetIssueComments(long repositoryId, int issueNumber)` + - `DeleteIssueComment(long repositoryId, long commentId)` + +2. **DeletePlusMessagesTrigger.cs**: New trigger that: + - Checks for comments containing only "+ " + - Verifies if 6 hours have passed since the last "+ " comment + - Deletes all "+ " comments from the issue + +3. **Program.cs**: Integrated the new trigger into the issue tracker + +## Testing Steps + +### Manual Testing (Recommended) +1. Create a test issue in a repository the bot monitors +2. Add several comments containing only "+ " with timestamps +3. Wait for the bot's polling interval (typically 60 seconds) +4. For comments older than 6 hours, verify they get deleted +5. For comments newer than 6 hours, verify they remain + +### Unit Testing Scenarios +1. **No Plus Comments**: Issue with no "+ " comments - should not trigger +2. **Recent Plus Comments**: Issue with "+ " comments less than 6 hours old - should not delete +3. **Old Plus Comments**: Issue with "+ " comments older than 6 hours - should delete all +4. **Mixed Comments**: Issue with both "+ " and regular comments - should only delete "+ " comments +5. **Error Handling**: Test API failures and network issues + +## Expected Behavior +- Only comments containing exactly "+ " (plus followed by space) are deleted +- Comments are only deleted if 6 hours have passed since the last "+ " comment +- All "+ " comments in the issue are deleted at once, not just the old ones +- Other comments remain untouched +- Bot logs successful deletions to console + +## Files Modified +- `csharp/Storage/RemoteStorage/GitHubStorage.cs`: Added comment management methods +- `csharp/Platform.Bot/Triggers/DeletePlusMessagesTrigger.cs`: New trigger implementation +- `csharp/Platform.Bot/Program.cs`: Integrated new trigger \ No newline at end of file