Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion csharp/Platform.Bot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private static async Task<int> 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();
Expand Down
164 changes: 164 additions & 0 deletions csharp/Platform.Bot/Triggers/SynonymRecognitionTrigger.cs
Original file line number Diff line number Diff line change
@@ -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;
/// <summary>
/// <para>
/// Represents the synonym recognition trigger that recognizes positive and negative synonyms.
/// </para>
/// <para></para>
/// </summary>
/// <seealso cref="ITrigger{TContext}"/>
internal class SynonymRecognitionTrigger : ITrigger<TContext>
{
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
"--", "-"
};

/// <summary>
/// <para>
/// Initializes a new <see cref="SynonymRecognitionTrigger"/> instance.
/// </para>
/// <para></para>
/// </summary>
/// <param name="storage">
/// <para>A git hub storage.</para>
/// <para></para>
/// </param>
public SynonymRecognitionTrigger(GitHubStorage storage)
{
this._storage = storage;
}

/// <summary>
/// <para>
/// Actions the context.
/// </para>
/// <para></para>
/// </summary>
/// <param name="context">
/// <para>The context.</para>
/// <para></para>
/// </param>
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);
}

/// <summary>
/// <para>
/// Determines whether this instance condition.
/// </para>
/// <para></para>
/// </summary>
/// <param name="context">
/// <para>The context.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>The bool</para>
/// <para></para>
/// </returns>
public async Task<bool> Condition(TContext context)
{
var text = $"{context.Title} {context.Body}".ToLower();
return ContainsAnySynonym(text);
}

/// <summary>
/// <para>
/// Gets the sentiment from the issue content.
/// </para>
/// <para></para>
/// </summary>
/// <param name="context">
/// <para>The context.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>The sentiment string</para>
/// <para></para>
/// </returns>
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";
}
}

/// <summary>
/// <para>
/// Checks if text contains any synonym.
/// </para>
/// <para></para>
/// </summary>
/// <param name="text">
/// <para>The text to check.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>True if contains any synonym</para>
/// <para></para>
/// </returns>
private bool ContainsAnySynonym(string text)
{
return _positiveSynonyms.Any(synonym => text.Contains(synonym.ToLower())) ||
_negativeSynonyms.Any(synonym => text.Contains(synonym.ToLower()));
}
}
}
154 changes: 154 additions & 0 deletions experiments/SynonymRecognitionTest.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// <para>
/// Simple test class to verify SynonymRecognitionTrigger functionality.
/// </para>
/// <para></para>
/// </summary>
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;
}
}
}
2 changes: 2 additions & 0 deletions experiments/SynonymTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
10 changes: 10 additions & 0 deletions experiments/SynonymTest/SynonymTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading