1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . IO ;
4+ using System . Linq ;
5+ using System . Text ;
6+ using System . Threading . Tasks ;
7+ using Interfaces ;
8+ using Octokit ;
9+ using OpenAI ;
10+ using OpenAI . Chat ;
11+ using Storage . Local ;
12+ using Storage . Remote . GitHub ;
13+
14+ namespace Platform . Bot . Triggers
15+ {
16+ using TContext = Issue ;
17+
18+ /// <summary>
19+ /// <para>
20+ /// Represents the code optimizer trigger that uses ChatGPT/GPT-4 to optimize code.
21+ /// </para>
22+ /// <para></para>
23+ /// </summary>
24+ /// <seealso cref="ITrigger{TContext}"/>
25+ internal class CodeOptimizerTrigger : ITrigger < TContext >
26+ {
27+ private readonly GitHubStorage _storage ;
28+ private readonly FileStorage _fileStorage ;
29+ private readonly OpenAIClient _openAiClient ;
30+ private readonly string _model ;
31+
32+ /// <summary>
33+ /// <para>
34+ /// Initializes a new <see cref="CodeOptimizerTrigger"/> instance.
35+ /// </para>
36+ /// <para></para>
37+ /// </summary>
38+ /// <param name="storage">
39+ /// <para>A GitHub storage.</para>
40+ /// <para></para>
41+ /// </param>
42+ /// <param name="fileStorage">
43+ /// <para>A file storage.</para>
44+ /// <para></para>
45+ /// </param>
46+ /// <param name="openAiApiKey">
47+ /// <para>OpenAI API key.</para>
48+ /// <para></para>
49+ /// </param>
50+ /// <param name="model">
51+ /// <para>The model to use (default: gpt-4).</para>
52+ /// <para></para>
53+ /// </param>
54+ public CodeOptimizerTrigger ( GitHubStorage storage , FileStorage fileStorage , string openAiApiKey , string model = "gpt-4" )
55+ {
56+ _storage = storage ?? throw new ArgumentNullException ( nameof ( storage ) ) ;
57+ _fileStorage = fileStorage ?? throw new ArgumentNullException ( nameof ( fileStorage ) ) ;
58+ _openAiClient = new OpenAIClient ( openAiApiKey ) ;
59+ _model = model ;
60+ }
61+
62+ /// <summary>
63+ /// <para>
64+ /// Determines whether this instance condition.
65+ /// </para>
66+ /// <para></para>
67+ /// </summary>
68+ /// <param name="context">
69+ /// <para>The context.</para>
70+ /// <para></para>
71+ /// </param>
72+ /// <returns>
73+ /// <para>The bool</para>
74+ /// <para></para>
75+ /// </returns>
76+ public async Task < bool > Condition ( TContext context )
77+ {
78+ var title = context . Title . ToLower ( ) ;
79+ var body = context . Body ? . ToLower ( ) ?? "" ;
80+
81+ return title . Contains ( "optimize" ) || title . Contains ( "optimization" ) ||
82+ body . Contains ( "optimize code" ) || body . Contains ( "code optimization" ) ||
83+ title . Contains ( "improve performance" ) || body . Contains ( "improve performance" ) ;
84+ }
85+
86+ /// <summary>
87+ /// <para>
88+ /// Actions the context.
89+ /// </para>
90+ /// <para></para>
91+ /// </summary>
92+ /// <param name="context">
93+ /// <para>The context.</para>
94+ /// <para></para>
95+ /// </param>
96+ public async Task Action ( TContext context )
97+ {
98+ try
99+ {
100+ var repository = context . Repository ;
101+ var optimizedFiles = new List < ( string path , string content ) > ( ) ;
102+
103+ // Get all code files from the repository
104+ var codeFiles = await GetCodeFilesFromRepository ( repository ) ;
105+
106+ foreach ( var file in codeFiles )
107+ {
108+ var optimizedCode = await OptimizeCodeWithGPT ( file . content , file . path ) ;
109+ if ( ! string . IsNullOrWhiteSpace ( optimizedCode ) && optimizedCode != file . content )
110+ {
111+ optimizedFiles . Add ( ( file . path , optimizedCode ) ) ;
112+ }
113+ }
114+
115+ // Create a new branch for optimizations
116+ var branchName = $ "optimize-code-{ context . Number } ";
117+ var defaultBranch = repository . DefaultBranch ;
118+
119+ // Create branch and commit optimized files
120+ if ( optimizedFiles . Any ( ) )
121+ {
122+ // Create branch reference
123+ var defaultBranchRef = await _storage . GetBranch ( repository . Id , defaultBranch ) ;
124+ var newReference = new NewReference ( $ "refs/heads/{ branchName } ", defaultBranchRef . Commit . Sha ) ;
125+ await _storage . CreateReference ( repository . Id , newReference ) ;
126+
127+ foreach ( var file in optimizedFiles )
128+ {
129+ await _storage . CreateOrUpdateFile ( file . content , repository , branchName , file . path ,
130+ $ "Optimize code in { file . path } using GPT-4") ;
131+ }
132+
133+ // Create pull request using Octokit client directly
134+ var newPr = new NewPullRequest (
135+ $ "Code optimization for issue #{ context . Number } ",
136+ branchName ,
137+ defaultBranch )
138+ {
139+ Body = $ "This PR contains code optimizations generated by GPT-4 for issue #{ context . Number } .\n \n " +
140+ $ "Files optimized:\n { string . Join ( "\n " , optimizedFiles . Select ( f => $ "- { f . path } ") ) } "
141+ } ;
142+
143+ await _storage . Client . PullRequest . Create ( repository . Id , newPr ) ;
144+
145+ // Add comment to the issue
146+ var comment = $ "I've analyzed the code and created optimizations using GPT-4. " +
147+ $ "Please check the pull request with the optimized code: { branchName } ";
148+ await _storage . CreateIssueComment ( repository . Id , context . Number , comment ) ;
149+ }
150+ else
151+ {
152+ await _storage . CreateIssueComment ( repository . Id , context . Number ,
153+ "I've analyzed the code but couldn't find any significant optimizations to suggest at this time." ) ;
154+ }
155+ }
156+ catch ( Exception ex )
157+ {
158+ await _storage . CreateIssueComment ( context . Repository . Id , context . Number ,
159+ $ "An error occurred while optimizing the code: { ex . Message } ") ;
160+ }
161+ }
162+
163+ private async Task < List < ( string path , string content ) > > GetCodeFilesFromRepository ( Repository repository )
164+ {
165+ var files = new List < ( string path , string content ) > ( ) ;
166+ var supportedExtensions = new [ ] { ".cs" , ".js" , ".ts" , ".py" , ".java" , ".cpp" , ".c" , ".h" } ;
167+
168+ try
169+ {
170+ // Get all contents from repository using Octokit client directly
171+ var contents = await _storage . Client . Repository . Content . GetAllContents ( repository . Id ) ;
172+
173+ foreach ( var content in contents . Where ( c => c . Type == ContentType . File ) )
174+ {
175+ var extension = Path . GetExtension ( content . Name ) ;
176+ if ( supportedExtensions . Contains ( extension , StringComparer . OrdinalIgnoreCase ) )
177+ {
178+ // Get file content
179+ var fileContents = await _storage . Client . Repository . Content . GetAllContentsByRef ( repository . Id , content . Path , repository . DefaultBranch ) ;
180+ var fileContent = fileContents . FirstOrDefault ( ) ? . Content ;
181+
182+ if ( ! string . IsNullOrWhiteSpace ( fileContent ) )
183+ {
184+ // Decode base64 content
185+ var decodedContent = Encoding . UTF8 . GetString ( Convert . FromBase64String ( fileContent ) ) ;
186+ files . Add ( ( content . Path , decodedContent ) ) ;
187+ }
188+ }
189+ }
190+ }
191+ catch ( Exception ex )
192+ {
193+ // Log error but continue
194+ Console . WriteLine ( $ "Error getting repository contents: { ex . Message } ") ;
195+ }
196+
197+ return files ;
198+ }
199+
200+ private async Task < string > OptimizeCodeWithGPT ( string code , string filePath )
201+ {
202+ try
203+ {
204+ var fileExtension = Path . GetExtension ( filePath ) ;
205+ var language = GetLanguageFromExtension ( fileExtension ) ;
206+
207+ var systemPrompt = $ "You are a code optimization expert. Analyze the provided { language } code and suggest optimizations for:" +
208+ "\n 1. Performance improvements" +
209+ "\n 2. Memory usage optimization" +
210+ "\n 3. Code readability and maintainability" +
211+ "\n 4. Best practices compliance" +
212+ "\n 5. Algorithmic improvements" +
213+ "\n \n Provide only the optimized code without explanations. If no significant optimizations are possible, return the original code." ;
214+
215+ var userPrompt = $ "Optimize this { language } code:\n \n ```{ language } \n { code } \n ```";
216+
217+ var chatClient = _openAiClient . GetChatClient ( _model ) ;
218+ var messages = new List < ChatMessage >
219+ {
220+ ChatMessage . CreateSystemMessage ( systemPrompt ) ,
221+ ChatMessage . CreateUserMessage ( userPrompt )
222+ } ;
223+
224+ var response = await chatClient . CompleteChatAsync ( messages ) ;
225+ var optimizedCode = response . Value . Content [ 0 ] . Text ;
226+
227+ // Clean up the response - remove markdown code blocks if present
228+ optimizedCode = CleanCodeResponse ( optimizedCode ) ;
229+
230+ return optimizedCode ;
231+ }
232+ catch ( Exception ex )
233+ {
234+ Console . WriteLine ( $ "Error optimizing code with GPT: { ex . Message } ") ;
235+ return code ; // Return original code if optimization fails
236+ }
237+ }
238+
239+ private string GetLanguageFromExtension ( string extension )
240+ {
241+ return extension . ToLower ( ) switch
242+ {
243+ ".cs" => "C#" ,
244+ ".js" => "JavaScript" ,
245+ ".ts" => "TypeScript" ,
246+ ".py" => "Python" ,
247+ ".java" => "Java" ,
248+ ".cpp" => "C++" ,
249+ ".c" => "C" ,
250+ ".h" => "C/C++ Header" ,
251+ _ => "code"
252+ } ;
253+ }
254+
255+ private string CleanCodeResponse ( string response )
256+ {
257+ if ( string . IsNullOrWhiteSpace ( response ) )
258+ return response ;
259+
260+ // Remove markdown code blocks
261+ var lines = response . Split ( '\n ' ) ;
262+ var codeLines = new List < string > ( ) ;
263+ bool inCodeBlock = false ;
264+
265+ foreach ( var line in lines )
266+ {
267+ if ( line . StartsWith ( "```" ) )
268+ {
269+ inCodeBlock = ! inCodeBlock ;
270+ continue ;
271+ }
272+
273+ if ( inCodeBlock || ! response . Contains ( "```" ) )
274+ {
275+ codeLines . Add ( line ) ;
276+ }
277+ }
278+
279+ return string . Join ( '\n ' , codeLines ) . Trim ( ) ;
280+ }
281+ }
282+ }
0 commit comments