Skip to content

Commit 46e9e76

Browse files
konardclaude
andcommitted
Add recognition of "+" and "-" synonyms
This commit implements recognition for synonyms of positive (+) and negative (-) sentiments as requested in issue #12. Features implemented: - Recognition for thumbs-up/down emojis (πŸ‘/πŸ‘Ž) and stickers (:thumbsup:/:thumbsdown:) - Support for expressions of gratitude ("Thank you", "Бпасибо", "Π‘Π»Π°Π³ΠΎΠ΄Π°Ρ€ΡŽ") - Recognition of agreement/disagreement expressions ("Agree"/"Disagree", "True"/"False", "Yes"/"No") - Support for increment/decrement operators ("++"/"--") - Multilingual support for Russian expressions The SynonymRecognitionTrigger analyzes issue titles and bodies for these synonyms and posts a comment with the recognized sentiment. The bot will now automatically detect and respond to issues containing these synonyms. πŸ€– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 02075f2 commit 46e9e76

File tree

5 files changed

+331
-1
lines changed

5 files changed

+331
-1
lines changed

β€Žcsharp/Platform.Bot/Program.csβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static async Task<int> Main(string[] args)
9595
var dbContext = new FileStorage(databaseFilePath?.FullName ?? new TemporaryFile().Filename);
9696
Console.WriteLine($"Bot has been started. {Environment.NewLine}Press CTRL+C to close");
9797
var githubStorage = new GitHubStorage(githubUserName, githubApiToken, githubApplicationName);
98-
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));
98+
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));
9999
var pullRequenstTracker = new PullRequestTracker(githubStorage, new MergeDependabotBumpsTrigger(githubStorage));
100100
var timestampTracker = new DateTimeTracker(githubStorage, new CreateAndSaveOrganizationRepositoriesMigrationTrigger(githubStorage, dbContext, Path.Combine(Directory.GetCurrentDirectory(), "/github-migrations")));
101101
var cancellation = new CancellationTokenSource();
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
using System.Threading.Tasks;
2+
using Interfaces;
3+
using Octokit;
4+
using Storage.Remote.GitHub;
5+
using System.Text.RegularExpressions;
6+
using System.Linq;
7+
8+
namespace Platform.Bot.Triggers
9+
{
10+
using TContext = Issue;
11+
/// <summary>
12+
/// <para>
13+
/// Represents the synonym recognition trigger that recognizes positive and negative synonyms.
14+
/// </para>
15+
/// <para></para>
16+
/// </summary>
17+
/// <seealso cref="ITrigger{TContext}"/>
18+
internal class SynonymRecognitionTrigger : ITrigger<TContext>
19+
{
20+
private readonly GitHubStorage _storage;
21+
22+
// Positive synonyms (equivalent to "+")
23+
private readonly string[] _positiveSynonyms = {
24+
// Thumbs-up emojis and Unicode
25+
"πŸ‘", "πŸ‘πŸ»", "πŸ‘πŸΌ", "πŸ‘πŸ½", "πŸ‘πŸΎ", "πŸ‘πŸΏ",
26+
":thumbsup:", ":+1:", ":thumbs_up:",
27+
28+
// Expressions of gratitude
29+
"thank you", "thanks", "спасибо", "Π±Π»Π°Π³ΠΎΠ΄Π°Ρ€ΡŽ",
30+
31+
// Expressions of agreement
32+
"agree", "true", "yes", "да", "согласСн", "согласна",
33+
34+
// Plus operators
35+
"++", "+"
36+
};
37+
38+
// Negative synonyms (equivalent to "-")
39+
private readonly string[] _negativeSynonyms = {
40+
// Thumbs-down emojis and Unicode
41+
"πŸ‘Ž", "πŸ‘ŽπŸ»", "πŸ‘ŽπŸΌ", "πŸ‘ŽπŸ½", "πŸ‘ŽπŸΎ", "πŸ‘ŽπŸΏ",
42+
":thumbsdown:", ":-1:", ":thumbs_down:",
43+
44+
// Expressions of disagreement
45+
"disagree", "false", "no", "Π½Π΅Ρ‚", "Π½Π΅ согласСн", "Π½Π΅ согласна",
46+
47+
// Minus operators
48+
"--", "-"
49+
};
50+
51+
/// <summary>
52+
/// <para>
53+
/// Initializes a new <see cref="SynonymRecognitionTrigger"/> instance.
54+
/// </para>
55+
/// <para></para>
56+
/// </summary>
57+
/// <param name="storage">
58+
/// <para>A git hub storage.</para>
59+
/// <para></para>
60+
/// </param>
61+
public SynonymRecognitionTrigger(GitHubStorage storage)
62+
{
63+
this._storage = storage;
64+
}
65+
66+
/// <summary>
67+
/// <para>
68+
/// Actions the context.
69+
/// </para>
70+
/// <para></para>
71+
/// </summary>
72+
/// <param name="context">
73+
/// <para>The context.</para>
74+
/// <para></para>
75+
/// </param>
76+
public async Task Action(TContext context)
77+
{
78+
var sentiment = GetSentiment(context);
79+
var comment = $"Recognized sentiment: {sentiment}";
80+
81+
// Create a comment on the issue with the recognized sentiment
82+
await _storage.Client.Issue.Comment.Create(context.Repository.Id, context.Number, comment);
83+
}
84+
85+
/// <summary>
86+
/// <para>
87+
/// Determines whether this instance condition.
88+
/// </para>
89+
/// <para></para>
90+
/// </summary>
91+
/// <param name="context">
92+
/// <para>The context.</para>
93+
/// <para></para>
94+
/// </param>
95+
/// <returns>
96+
/// <para>The bool</para>
97+
/// <para></para>
98+
/// </returns>
99+
public async Task<bool> Condition(TContext context)
100+
{
101+
var text = $"{context.Title} {context.Body}".ToLower();
102+
return ContainsAnySynonym(text);
103+
}
104+
105+
/// <summary>
106+
/// <para>
107+
/// Gets the sentiment from the issue content.
108+
/// </para>
109+
/// <para></para>
110+
/// </summary>
111+
/// <param name="context">
112+
/// <para>The context.</para>
113+
/// <para></para>
114+
/// </param>
115+
/// <returns>
116+
/// <para>The sentiment string</para>
117+
/// <para></para>
118+
/// </returns>
119+
private string GetSentiment(TContext context)
120+
{
121+
var text = $"{context.Title} {context.Body}".ToLower();
122+
123+
var positiveMatches = _positiveSynonyms.Count(synonym => text.Contains(synonym.ToLower()));
124+
var negativeMatches = _negativeSynonyms.Count(synonym => text.Contains(synonym.ToLower()));
125+
126+
if (positiveMatches > negativeMatches)
127+
{
128+
return "Positive (+)";
129+
}
130+
else if (negativeMatches > positiveMatches)
131+
{
132+
return "Negative (-)";
133+
}
134+
else if (positiveMatches == negativeMatches && positiveMatches > 0)
135+
{
136+
return "Neutral (mixed positive and negative)";
137+
}
138+
else
139+
{
140+
return "Unknown";
141+
}
142+
}
143+
144+
/// <summary>
145+
/// <para>
146+
/// Checks if text contains any synonym.
147+
/// </para>
148+
/// <para></para>
149+
/// </summary>
150+
/// <param name="text">
151+
/// <para>The text to check.</para>
152+
/// <para></para>
153+
/// </param>
154+
/// <returns>
155+
/// <para>True if contains any synonym</para>
156+
/// <para></para>
157+
/// </returns>
158+
private bool ContainsAnySynonym(string text)
159+
{
160+
return _positiveSynonyms.Any(synonym => text.Contains(synonym.ToLower())) ||
161+
_negativeSynonyms.Any(synonym => text.Contains(synonym.ToLower()));
162+
}
163+
}
164+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using Octokit;
5+
using Platform.Bot.Triggers;
6+
using Storage.Remote.GitHub;
7+
8+
namespace Platform.Bot.Experiments
9+
{
10+
/// <summary>
11+
/// <para>
12+
/// Simple test class to verify SynonymRecognitionTrigger functionality.
13+
/// </para>
14+
/// <para></para>
15+
/// </summary>
16+
public class SynonymRecognitionTest
17+
{
18+
public static void Main(string[] args)
19+
{
20+
Console.WriteLine("=== Synonym Recognition Test ===");
21+
22+
// Test with mock issues - we can't create actual GitHub API calls without credentials
23+
TestPositiveSynonyms();
24+
TestNegativeSynonyms();
25+
TestMixedSynonyms();
26+
TestNoSynonyms();
27+
28+
Console.WriteLine("All tests completed successfully!");
29+
}
30+
31+
private static void TestPositiveSynonyms()
32+
{
33+
Console.WriteLine("\n--- Testing Positive Synonyms ---");
34+
35+
var testCases = new[]
36+
{
37+
"Thank you for the help!",
38+
"This looks great πŸ‘",
39+
"I agree with this approach",
40+
"Yes, this is correct",
41+
"++ for this feature",
42+
"Бпасибо Π·Π° ΠΏΠΎΠΌΠΎΡ‰ΡŒ!",
43+
":thumbsup: Great work!",
44+
"True, this makes sense"
45+
};
46+
47+
foreach (var testCase in testCases)
48+
{
49+
var result = CheckForSynonyms(testCase, testCase);
50+
Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}");
51+
}
52+
}
53+
54+
private static void TestNegativeSynonyms()
55+
{
56+
Console.WriteLine("\n--- Testing Negative Synonyms ---");
57+
58+
var testCases = new[]
59+
{
60+
"I disagree with this",
61+
"This is false πŸ‘Ž",
62+
"No, this won't work",
63+
"-- for this approach",
64+
"НСт, Π½Π΅ согласСн",
65+
":thumbsdown: Not good",
66+
"This is incorrect - false"
67+
};
68+
69+
foreach (var testCase in testCases)
70+
{
71+
var result = CheckForSynonyms(testCase, testCase);
72+
Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}");
73+
}
74+
}
75+
76+
private static void TestMixedSynonyms()
77+
{
78+
Console.WriteLine("\n--- Testing Mixed Synonyms ---");
79+
80+
var testCases = new[]
81+
{
82+
"Thank you, but I disagree",
83+
"πŸ‘ Some good points, but πŸ‘Ž others",
84+
"Yes and no - mixed feelings"
85+
};
86+
87+
foreach (var testCase in testCases)
88+
{
89+
var result = CheckForSynonyms(testCase, testCase);
90+
Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}");
91+
}
92+
}
93+
94+
private static void TestNoSynonyms()
95+
{
96+
Console.WriteLine("\n--- Testing No Synonyms ---");
97+
98+
var testCases = new[]
99+
{
100+
"This is a regular comment",
101+
"Please review this code",
102+
"What do you think about this implementation?",
103+
"Let me know when this is ready"
104+
};
105+
106+
foreach (var testCase in testCases)
107+
{
108+
var result = CheckForSynonyms(testCase, testCase);
109+
Console.WriteLine($"Input: '{testCase}' -> Contains synonyms: {result}");
110+
}
111+
}
112+
113+
// Simplified version of synonym checking logic
114+
private static bool CheckForSynonyms(string title, string body)
115+
{
116+
var text = $"{title} {body}".ToLower();
117+
118+
// Positive synonyms (equivalent to "+")
119+
string[] positiveSynonyms = {
120+
"πŸ‘", "πŸ‘πŸ»", "πŸ‘πŸΌ", "πŸ‘πŸ½", "πŸ‘πŸΎ", "πŸ‘πŸΏ",
121+
":thumbsup:", ":+1:", ":thumbs_up:",
122+
"thank you", "thanks", "спасибо", "Π±Π»Π°Π³ΠΎΠ΄Π°Ρ€ΡŽ",
123+
"agree", "true", "yes", "да", "согласСн", "согласна",
124+
"++", "+"
125+
};
126+
127+
// Negative synonyms (equivalent to "-")
128+
string[] negativeSynonyms = {
129+
"πŸ‘Ž", "πŸ‘ŽπŸ»", "πŸ‘ŽπŸΌ", "πŸ‘ŽπŸ½", "πŸ‘ŽπŸΎ", "πŸ‘ŽπŸΏ",
130+
":thumbsdown:", ":-1:", ":thumbs_down:",
131+
"disagree", "false", "no", "Π½Π΅Ρ‚", "Π½Π΅ согласСн", "Π½Π΅ согласна",
132+
"--", "-"
133+
};
134+
135+
foreach (var synonym in positiveSynonyms)
136+
{
137+
if (text.Contains(synonym.ToLower()))
138+
{
139+
return true;
140+
}
141+
}
142+
143+
foreach (var synonym in negativeSynonyms)
144+
{
145+
if (text.Contains(synonym.ToLower()))
146+
{
147+
return true;
148+
}
149+
}
150+
151+
return false;
152+
}
153+
}
154+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ο»Ώ// See https://aka.ms/new-console-template for more information
2+
Console.WriteLine("Hello, World!");
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

0 commit comments

Comments
Β (0)