diff --git a/csharp/Platform.Bot/Program.cs b/csharp/Platform.Bot/Program.cs index 521a6b95..9884b4ce 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 SynonymRecognitionTrigger(githubStorage), 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 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/SynonymRecognitionTrigger.cs b/csharp/Platform.Bot/Triggers/SynonymRecognitionTrigger.cs new file mode 100644 index 00000000..6b8eb7b0 --- /dev/null +++ b/csharp/Platform.Bot/Triggers/SynonymRecognitionTrigger.cs @@ -0,0 +1,164 @@ +using System.Threading.Tasks; +using Interfaces; +using Octokit; +using Storage.Remote.GitHub; +using System.Text.RegularExpressions; +using System.Linq; + +namespace Platform.Bot.Triggers +{ + using TContext = Issue; + /// + /// + /// Represents the synonym recognition trigger that recognizes positive and negative synonyms. + /// + /// + /// + /// + internal class SynonymRecognitionTrigger : ITrigger + { + private readonly GitHubStorage _storage; + + // Positive synonyms (equivalent to "+") + private readonly string[] _positiveSynonyms = { + // Thumbs-up emojis and Unicode + "👍", "👍🏻", "👍🏼", "👍🏽", "👍🏾", "👍🏿", + ":thumbsup:", ":+1:", ":thumbs_up:", + + // Expressions of gratitude + "thank you", "thanks", "спасибо", "благодарю", + + // Expressions of agreement + "agree", "true", "yes", "да", "согласен", "согласна", + + // Plus operators + "++", "+" + }; + + // Negative synonyms (equivalent to "-") + private readonly string[] _negativeSynonyms = { + // Thumbs-down emojis and Unicode + "👎", "👎🏻", "👎🏼", "👎🏽", "👎🏾", "👎🏿", + ":thumbsdown:", ":-1:", ":thumbs_down:", + + // Expressions of disagreement + "disagree", "false", "no", "нет", "не согласен", "не согласна", + + // Minus operators + "--", "-" + }; + + /// + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// A git hub storage. + /// + /// + public SynonymRecognitionTrigger(GitHubStorage storage) + { + this._storage = storage; + } + + /// + /// + /// Actions the context. + /// + /// + /// + /// + /// The context. + /// + /// + public async Task Action(TContext context) + { + var sentiment = GetSentiment(context); + var comment = $"Recognized sentiment: {sentiment}"; + + // Create a comment on the issue with the recognized sentiment + await _storage.Client.Issue.Comment.Create(context.Repository.Id, context.Number, comment); + } + + /// + /// + /// Determines whether this instance condition. + /// + /// + /// + /// + /// The context. + /// + /// + /// + /// The bool + /// + /// + public async Task Condition(TContext context) + { + var text = $"{context.Title} {context.Body}".ToLower(); + return ContainsAnySynonym(text); + } + + /// + /// + /// Gets the sentiment from the issue content. + /// + /// + /// + /// + /// The context. + /// + /// + /// + /// The sentiment string + /// + /// + private string GetSentiment(TContext context) + { + var text = $"{context.Title} {context.Body}".ToLower(); + + var positiveMatches = _positiveSynonyms.Count(synonym => text.Contains(synonym.ToLower())); + var negativeMatches = _negativeSynonyms.Count(synonym => text.Contains(synonym.ToLower())); + + if (positiveMatches > negativeMatches) + { + return "Positive (+)"; + } + else if (negativeMatches > positiveMatches) + { + return "Negative (-)"; + } + else if (positiveMatches == negativeMatches && positiveMatches > 0) + { + return "Neutral (mixed positive and negative)"; + } + else + { + return "Unknown"; + } + } + + /// + /// + /// Checks if text contains any synonym. + /// + /// + /// + /// + /// The text to check. + /// + /// + /// + /// True if contains any synonym + /// + /// + private bool ContainsAnySynonym(string text) + { + return _positiveSynonyms.Any(synonym => text.Contains(synonym.ToLower())) || + _negativeSynonyms.Any(synonym => text.Contains(synonym.ToLower())); + } + } +} \ No newline at end of file diff --git a/experiments/SynonymRecognitionTest.cs b/experiments/SynonymRecognitionTest.cs new file mode 100644 index 00000000..39f62721 --- /dev/null +++ b/experiments/SynonymRecognitionTest.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Octokit; +using Platform.Bot.Triggers; +using Storage.Remote.GitHub; + +namespace Platform.Bot.Experiments +{ + /// + /// + /// Simple test class to verify SynonymRecognitionTrigger functionality. + /// + /// + /// + public class SynonymRecognitionTest + { + public static void Main(string[] args) + { + Console.WriteLine("=== Synonym Recognition Test ==="); + + // Test with mock issues - we can't create actual GitHub API calls without credentials + TestPositiveSynonyms(); + TestNegativeSynonyms(); + TestMixedSynonyms(); + TestNoSynonyms(); + + Console.WriteLine("All tests completed successfully!"); + } + + private static void TestPositiveSynonyms() + { + Console.WriteLine("\n--- Testing Positive Synonyms ---"); + + var testCases = new[] + { + "Thank you for the help!", + "This looks great 👍", + "I agree with this approach", + "Yes, this is correct", + "++ for this feature", + "Спасибо за помощь!", + ":thumbsup: Great work!", + "True, this makes sense" + }; + + foreach (var testCase in testCases) + { + var result = CheckForSynonyms(testCase, testCase); + Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}"); + } + } + + private static void TestNegativeSynonyms() + { + Console.WriteLine("\n--- Testing Negative Synonyms ---"); + + var testCases = new[] + { + "I disagree with this", + "This is false 👎", + "No, this won't work", + "-- for this approach", + "Нет, не согласен", + ":thumbsdown: Not good", + "This is incorrect - false" + }; + + foreach (var testCase in testCases) + { + var result = CheckForSynonyms(testCase, testCase); + Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}"); + } + } + + private static void TestMixedSynonyms() + { + Console.WriteLine("\n--- Testing Mixed Synonyms ---"); + + var testCases = new[] + { + "Thank you, but I disagree", + "👍 Some good points, but 👎 others", + "Yes and no - mixed feelings" + }; + + foreach (var testCase in testCases) + { + var result = CheckForSynonyms(testCase, testCase); + Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}"); + } + } + + private static void TestNoSynonyms() + { + Console.WriteLine("\n--- Testing No Synonyms ---"); + + var testCases = new[] + { + "This is a regular comment", + "Please review this code", + "What do you think about this implementation?", + "Let me know when this is ready" + }; + + foreach (var testCase in testCases) + { + var result = CheckForSynonyms(testCase, testCase); + Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}"); + } + } + + // Simplified version of synonym checking logic + private static bool CheckForSynonyms(string title, string body) + { + var text = $"{title} {body}".ToLower(); + + // Positive synonyms (equivalent to "+") + string[] positiveSynonyms = { + "👍", "👍🏻", "👍🏼", "👍🏽", "👍🏾", "👍🏿", + ":thumbsup:", ":+1:", ":thumbs_up:", + "thank you", "thanks", "спасибо", "благодарю", + "agree", "true", "yes", "да", "согласен", "согласна", + "++", "+" + }; + + // Negative synonyms (equivalent to "-") + string[] negativeSynonyms = { + "👎", "👎🏻", "👎🏼", "👎🏽", "👎🏾", "👎🏿", + ":thumbsdown:", ":-1:", ":thumbs_down:", + "disagree", "false", "no", "нет", "не согласен", "не согласна", + "--", "-" + }; + + foreach (var synonym in positiveSynonyms) + { + if (text.Contains(synonym.ToLower())) + { + return true; + } + } + + foreach (var synonym in negativeSynonyms) + { + if (text.Contains(synonym.ToLower())) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/experiments/SynonymTest/Program.cs b/experiments/SynonymTest/Program.cs new file mode 100644 index 00000000..3751555c --- /dev/null +++ b/experiments/SynonymTest/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/experiments/SynonymTest/SynonymTest.csproj b/experiments/SynonymTest/SynonymTest.csproj new file mode 100644 index 00000000..2150e379 --- /dev/null +++ b/experiments/SynonymTest/SynonymTest.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + +