Skip to content

Commit d3f3a38

Browse files
konardclaude
andcommitted
Implement rewards system with karma-based permissions
- Add Reward and UserKarma data models for tracking rewards and user karma - Extend FileStorage with methods to persist and manage rewards/karma data - Create KarmaService for user karma management and permission checking - Create RewardsService for managing reward issue tracking and validation - Add AddRewardTrigger for adding rewards with karma permission checks - Add ListRewardsTrigger for displaying available rewards - Add RemoveRewardTrigger for removing rewards with karma permission checks - Add CleanupClosedRewardsTrigger for automatic cleanup of closed issue rewards - Add KarmaAuthorTriggerDecorator for karma-based permission enforcement - Update Program.cs to register all reward-related triggers Features: - Users with N+ karma can add/remove rewards for GitHub issues - Automatic cleanup of rewards when issues are closed - Issue validation to ensure rewards are only added for valid, accessible issues - Persistent storage of rewards and karma data - Commands: "add reward <url> <description>", "remove reward <url>", "list rewards", "cleanup rewards" 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 34cc1a6 commit d3f3a38

File tree

11 files changed

+1073
-1
lines changed

11 files changed

+1073
-1
lines changed

csharp/Platform.Bot/Program.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Platform.Bot.Trackers;
1616
using Platform.Bot.Triggers;
1717
using Platform.Bot.Triggers.Decorators;
18+
using Platform.Bot.Services;
1819

1920
namespace Platform.Bot
2021
{
@@ -95,7 +96,23 @@ private static async Task<int> Main(string[] args)
9596
var dbContext = new FileStorage(databaseFilePath?.FullName ?? new TemporaryFile().Filename);
9697
Console.WriteLine($"Bot has been started. {Environment.NewLine}Press CTRL+C to close");
9798
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));
99+
100+
// Initialize services
101+
var karmaService = new KarmaService(dbContext, githubStorage);
102+
var rewardsService = new RewardsService(dbContext, githubStorage);
103+
104+
var issueTracker = new IssueTracker(githubStorage,
105+
new HelloWorldTrigger(githubStorage, dbContext, fileSetName),
106+
new OrganizationLastMonthActivityTrigger(githubStorage),
107+
new LastCommitActivityTrigger(githubStorage),
108+
new AdminAuthorIssueTriggerDecorator(new ProtectDefaultBranchTrigger(githubStorage), githubStorage),
109+
new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationRepositoriesDefaultBranchTrigger(githubStorage, dbContext), githubStorage),
110+
new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationPullRequestsBaseBranchTrigger(githubStorage, dbContext), githubStorage),
111+
// Reward-related triggers
112+
new KarmaAuthorTriggerDecorator(new AddRewardTrigger(githubStorage, rewardsService, karmaService), karmaService, githubStorage),
113+
new ListRewardsTrigger(githubStorage, rewardsService),
114+
new KarmaAuthorTriggerDecorator(new RemoveRewardTrigger(githubStorage, rewardsService, karmaService), karmaService, githubStorage),
115+
new CleanupClosedRewardsTrigger(githubStorage, rewardsService));
99116
var pullRequenstTracker = new PullRequestTracker(githubStorage, new MergeDependabotBumpsTrigger(githubStorage));
100117
var timestampTracker = new DateTimeTracker(githubStorage, new CreateAndSaveOrganizationRepositoriesMigrationTrigger(githubStorage, dbContext, Path.Combine(Directory.GetCurrentDirectory(), "/github-migrations")));
101118
var cancellation = new CancellationTokenSource();
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using Storage.Local;
2+
using Storage.Remote.GitHub;
3+
using System;
4+
using System.Threading.Tasks;
5+
using Octokit;
6+
7+
namespace Platform.Bot.Services
8+
{
9+
/// <summary>
10+
/// <para>
11+
/// Service for managing user karma.
12+
/// </para>
13+
/// <para></para>
14+
/// </summary>
15+
public class KarmaService
16+
{
17+
private readonly FileStorage _fileStorage;
18+
private readonly GitHubStorage _gitHubStorage;
19+
private const int MinimumKarmaForRewards = 10; // Default minimum karma
20+
21+
/// <summary>
22+
/// <para>
23+
/// Initializes a new <see cref="KarmaService"/> instance.
24+
/// </para>
25+
/// <para></para>
26+
/// </summary>
27+
/// <param name="fileStorage">The file storage.</param>
28+
/// <param name="gitHubStorage">The GitHub storage.</param>
29+
public KarmaService(FileStorage fileStorage, GitHubStorage gitHubStorage)
30+
{
31+
_fileStorage = fileStorage;
32+
_gitHubStorage = gitHubStorage;
33+
}
34+
35+
/// <summary>
36+
/// <para>
37+
/// Checks if a user has sufficient karma to add rewards.
38+
/// </para>
39+
/// <para></para>
40+
/// </summary>
41+
/// <param name="username">The username to check.</param>
42+
/// <returns>True if user has sufficient karma.</returns>
43+
public bool HasSufficientKarma(string username)
44+
{
45+
var userKarma = _fileStorage.GetUserKarma(username);
46+
return userKarma?.KarmaPoints >= MinimumKarmaForRewards;
47+
}
48+
49+
/// <summary>
50+
/// <para>
51+
/// Gets or initializes user karma.
52+
/// </para>
53+
/// <para></para>
54+
/// </summary>
55+
/// <param name="username">The username.</param>
56+
/// <returns>The user karma.</returns>
57+
public UserKarma GetOrInitializeUserKarma(string username)
58+
{
59+
var karma = _fileStorage.GetUserKarma(username);
60+
if (karma == null)
61+
{
62+
karma = new UserKarma
63+
{
64+
Username = username,
65+
KarmaPoints = 0,
66+
LastUpdated = DateTime.UtcNow
67+
};
68+
_fileStorage.SetUserKarma(karma);
69+
}
70+
return karma;
71+
}
72+
73+
/// <summary>
74+
/// <para>
75+
/// Updates user karma points.
76+
/// </para>
77+
/// <para></para>
78+
/// </summary>
79+
/// <param name="username">The username.</param>
80+
/// <param name="points">Points to add (can be negative).</param>
81+
public void UpdateUserKarma(string username, int points)
82+
{
83+
var karma = GetOrInitializeUserKarma(username);
84+
karma.KarmaPoints += points;
85+
karma.LastUpdated = DateTime.UtcNow;
86+
_fileStorage.SetUserKarma(karma);
87+
}
88+
89+
/// <summary>
90+
/// <para>
91+
/// Calculates and updates user karma based on GitHub activity.
92+
/// </para>
93+
/// <para></para>
94+
/// </summary>
95+
/// <param name="username">The username.</param>
96+
/// <returns>Updated karma points.</returns>
97+
public async Task<int> CalculateAndUpdateKarmaFromActivity(string username)
98+
{
99+
try
100+
{
101+
// This is a simplified karma calculation based on contribution activity
102+
// In a real implementation, you might want to calculate based on:
103+
// - Number of merged pull requests
104+
// - Number of issues resolved
105+
// - Repository stars/forks
106+
// - Community engagement
107+
108+
var karma = GetOrInitializeUserKarma(username);
109+
var user = await _gitHubStorage.Client.User.Get(username);
110+
111+
// Simple karma calculation based on public repos and followers
112+
// This is a basic implementation - you might want to enhance it
113+
var calculatedKarma = (user.PublicRepos * 2) + (user.Followers / 10);
114+
115+
// Only update if the calculated karma is higher (to prevent karma loss)
116+
if (calculatedKarma > karma.KarmaPoints)
117+
{
118+
karma.KarmaPoints = calculatedKarma;
119+
karma.LastUpdated = DateTime.UtcNow;
120+
_fileStorage.SetUserKarma(karma);
121+
}
122+
123+
return karma.KarmaPoints;
124+
}
125+
catch (Exception)
126+
{
127+
// If we can't fetch user data, return existing karma
128+
var karma = GetOrInitializeUserKarma(username);
129+
return karma.KarmaPoints;
130+
}
131+
}
132+
133+
/// <summary>
134+
/// <para>
135+
/// Gets the minimum karma required for adding rewards.
136+
/// </para>
137+
/// <para></para>
138+
/// </summary>
139+
/// <returns>The minimum karma value.</returns>
140+
public int GetMinimumKarmaForRewards() => MinimumKarmaForRewards;
141+
}
142+
}

0 commit comments

Comments
 (0)