1+ using Discord ;
2+ using Discord . Commands ;
3+ using DiscordBot . Services ;
4+ using Microsoft . Extensions . Logging ;
5+ using System ;
6+ using System . Linq ;
7+ using System . Threading . Tasks ;
8+
9+ namespace DiscordBot . Modules
10+ {
11+ public class LanguageRoleModule : ModuleBase < SocketCommandContext >
12+ {
13+ private readonly DiscordBotService _botService ;
14+ private readonly GitHubLanguageDetectionService _languageService ;
15+ private readonly ILogger < LanguageRoleModule > _logger ;
16+
17+ public LanguageRoleModule (
18+ DiscordBotService botService ,
19+ GitHubLanguageDetectionService languageService ,
20+ ILogger < LanguageRoleModule > logger )
21+ {
22+ _botService = botService ;
23+ _languageService = languageService ;
24+ _logger = logger ;
25+ }
26+
27+ [ Command ( "sync-roles" ) ]
28+ [ Summary ( "Synchronizes your Discord roles with programming languages from your GitHub profile" ) ]
29+ public async Task SyncRolesAsync ( [ Summary ( "Your GitHub username" ) ] string githubUsername )
30+ {
31+ try
32+ {
33+ await Context . Channel . TriggerTypingAsync ( ) ;
34+
35+ _logger . LogInformation ( "User {DiscordUser} requested role sync with GitHub user {GitHubUser}" ,
36+ Context . User . Username , githubUsername ) ;
37+
38+ var embed = new EmbedBuilder ( )
39+ . WithTitle ( "🔄 Synchronizing Roles..." )
40+ . WithDescription ( $ "Analyzing GitHub profile: **{ githubUsername } **\n This may take a moment...")
41+ . WithColor ( Color . Blue )
42+ . WithTimestamp ( DateTimeOffset . Now )
43+ . Build ( ) ;
44+
45+ var message = await ReplyAsync ( embed : embed ) ;
46+
47+ _botService . StoreUserGitHubMapping ( Context . User . Id , githubUsername ) ;
48+
49+ var languageStats = await _languageService . GetUserProgrammingLanguagesAsync ( githubUsername ) ;
50+
51+ if ( ! languageStats . Any ( ) )
52+ {
53+ var errorEmbed = new EmbedBuilder ( )
54+ . WithTitle ( "❌ No Programming Languages Found" )
55+ . WithDescription ( $ "No programming languages found for GitHub user **{ githubUsername } **.\n \n " +
56+ "This could mean:\n " +
57+ "• The user doesn't exist\n " +
58+ "• The user has no public repositories\n " +
59+ "• The repositories don't contain detectable programming languages" )
60+ . WithColor ( Color . Red )
61+ . WithTimestamp ( DateTimeOffset . Now )
62+ . Build ( ) ;
63+
64+ await message . ModifyAsync ( m => m . Embed = errorEmbed ) ;
65+ return ;
66+ }
67+
68+ await _botService . SyncUserRolesAsync ( Context . User . Id , githubUsername ) ;
69+
70+ var topLanguages = GitHubLanguageDetectionService . GetTopLanguages ( languageStats ) ;
71+ var languageList = topLanguages . Take ( 10 ) . Select ( lang =>
72+ {
73+ var bytes = languageStats [ lang ] ;
74+ var size = bytes < 1024 ? $ "{ bytes } B" :
75+ bytes < 1024 * 1024 ? $ "{ bytes / 1024 : N0} KB" :
76+ $ "{ bytes / ( 1024 * 1024 ) : N1} MB";
77+ return $ "• **{ lang } ** ({ size } )";
78+ } ) ;
79+
80+ var successEmbed = new EmbedBuilder ( )
81+ . WithTitle ( "✅ Roles Synchronized Successfully!" )
82+ . WithDescription ( $ "**GitHub Profile:** { githubUsername } \n \n " +
83+ $ "**Top Programming Languages:**\n { string . Join ( "\n " , languageList ) } \n \n " +
84+ "Your Discord roles have been updated to reflect your programming language expertise!" )
85+ . WithColor ( Color . Green )
86+ . WithTimestamp ( DateTimeOffset . Now )
87+ . WithFooter ( "Roles are synchronized based on your public repositories" )
88+ . Build ( ) ;
89+
90+ await message . ModifyAsync ( m => m . Embed = successEmbed ) ;
91+ }
92+ catch ( Exception ex )
93+ {
94+ _logger . LogError ( ex , "Error during role synchronization for user {User} with GitHub {GitHub}" ,
95+ Context . User . Username , githubUsername ) ;
96+
97+ var errorEmbed = new EmbedBuilder ( )
98+ . WithTitle ( "❌ Synchronization Failed" )
99+ . WithDescription ( "An error occurred while synchronizing your roles. Please try again later or contact an administrator." )
100+ . WithColor ( Color . Red )
101+ . WithTimestamp ( DateTimeOffset . Now )
102+ . Build ( ) ;
103+
104+ await ReplyAsync ( embed : errorEmbed ) ;
105+ }
106+ }
107+
108+ [ Command ( "check-languages" ) ]
109+ [ Summary ( "Shows programming languages detected from a GitHub profile without changing roles" ) ]
110+ public async Task CheckLanguagesAsync ( [ Summary ( "GitHub username to check" ) ] string githubUsername )
111+ {
112+ try
113+ {
114+ await Context . Channel . TriggerTypingAsync ( ) ;
115+
116+ _logger . LogInformation ( "User {DiscordUser} requested language check for GitHub user {GitHubUser}" ,
117+ Context . User . Username , githubUsername ) ;
118+
119+ var embed = new EmbedBuilder ( )
120+ . WithTitle ( "🔍 Analyzing GitHub Profile..." )
121+ . WithDescription ( $ "Scanning repositories for: **{ githubUsername } **")
122+ . WithColor ( Color . Blue )
123+ . WithTimestamp ( DateTimeOffset . Now )
124+ . Build ( ) ;
125+
126+ var message = await ReplyAsync ( embed : embed ) ;
127+
128+ var languageStats = await _languageService . GetUserProgrammingLanguagesAsync ( githubUsername ) ;
129+
130+ if ( ! languageStats . Any ( ) )
131+ {
132+ var errorEmbed = new EmbedBuilder ( )
133+ . WithTitle ( "❌ No Programming Languages Found" )
134+ . WithDescription ( $ "No programming languages detected for GitHub user **{ githubUsername } **.")
135+ . WithColor ( Color . Red )
136+ . WithTimestamp ( DateTimeOffset . Now )
137+ . Build ( ) ;
138+
139+ await message . ModifyAsync ( m => m . Embed = errorEmbed ) ;
140+ return ;
141+ }
142+
143+ var topLanguages = GitHubLanguageDetectionService . GetTopLanguages ( languageStats , maxLanguages : 15 ) ;
144+ var totalBytes = languageStats . Values . Sum ( ) ;
145+
146+ var languageList = topLanguages . Select ( ( lang , index ) =>
147+ {
148+ var bytes = languageStats [ lang ] ;
149+ var percentage = ( double ) bytes / totalBytes * 100 ;
150+ var size = bytes < 1024 ? $ "{ bytes } B" :
151+ bytes < 1024 * 1024 ? $ "{ bytes / 1024 : N0} KB" :
152+ $ "{ bytes / ( 1024 * 1024 ) : N1} MB";
153+ return $ "{ index + 1 } . **{ lang } ** - { percentage : F1} % ({ size } )";
154+ } ) ;
155+
156+ var resultEmbed = new EmbedBuilder ( )
157+ . WithTitle ( $ "📊 Programming Languages for { githubUsername } ")
158+ . WithDescription ( $ "**Total Repositories Analyzed:** { languageStats . Count } \n \n " +
159+ $ "**Languages Found:**\n { string . Join ( "\n " , languageList ) } ")
160+ . WithColor ( Color . Blue )
161+ . WithTimestamp ( DateTimeOffset . Now )
162+ . WithFooter ( "Analysis based on public repositories only" )
163+ . Build ( ) ;
164+
165+ await message . ModifyAsync ( m => m . Embed = resultEmbed ) ;
166+ }
167+ catch ( Exception ex )
168+ {
169+ _logger . LogError ( ex , "Error during language check for GitHub user {GitHub}" , githubUsername ) ;
170+
171+ var errorEmbed = new EmbedBuilder ( )
172+ . WithTitle ( "❌ Analysis Failed" )
173+ . WithDescription ( "An error occurred while analyzing the GitHub profile. Please check the username and try again." )
174+ . WithColor ( Color . Red )
175+ . WithTimestamp ( DateTimeOffset . Now )
176+ . Build ( ) ;
177+
178+ await ReplyAsync ( embed : errorEmbed ) ;
179+ }
180+ }
181+
182+ [ Command ( "my-sync" ) ]
183+ [ Summary ( "Synchronizes your roles using your previously linked GitHub account" ) ]
184+ public async Task MySyncAsync ( )
185+ {
186+ var githubUsername = _botService . GetUserGitHubUsername ( Context . User . Id ) ;
187+
188+ if ( string . IsNullOrEmpty ( githubUsername ) )
189+ {
190+ var embed = new EmbedBuilder ( )
191+ . WithTitle ( "❌ No GitHub Account Linked" )
192+ . WithDescription ( "You haven't linked a GitHub account yet. Use:\n `!sync-roles <your-github-username>`" )
193+ . WithColor ( Color . Red )
194+ . WithTimestamp ( DateTimeOffset . Now )
195+ . Build ( ) ;
196+
197+ await ReplyAsync ( embed : embed ) ;
198+ return ;
199+ }
200+
201+ await SyncRolesAsync ( githubUsername ) ;
202+ }
203+
204+ [ Command ( "help" ) ]
205+ [ Summary ( "Shows available commands for role synchronization" ) ]
206+ public async Task HelpAsync ( )
207+ {
208+ var embed = new EmbedBuilder ( )
209+ . WithTitle ( "🤖 Programming Language Role Bot - Commands" )
210+ . WithDescription ( "This bot automatically assigns Discord roles based on your GitHub programming language usage." )
211+ . WithColor ( Color . Blue )
212+ . AddField ( "!sync-roles <github-username>" ,
213+ "Links your GitHub account and synchronizes your Discord roles" , false )
214+ . AddField ( "!my-sync" ,
215+ "Re-synchronizes your roles using your previously linked GitHub account" , false )
216+ . AddField ( "!check-languages <github-username>" ,
217+ "Shows programming languages for a GitHub user without changing roles" , false )
218+ . AddField ( "!help" ,
219+ "Shows this help message" , false )
220+ . WithTimestamp ( DateTimeOffset . Now )
221+ . WithFooter ( "LinksPlatform Discord Bot" )
222+ . Build ( ) ;
223+
224+ await ReplyAsync ( embed : embed ) ;
225+ }
226+ }
227+ }
0 commit comments