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
89 changes: 89 additions & 0 deletions csharp/Interfaces/IDataService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Bot.Interfaces
{
/// <summary>
/// Interface for data service operations, particularly for managing GitHub Copilot requests.
/// </summary>
public interface IDataService
{
/// <summary>
/// Enqueues a GitHub Copilot request.
/// </summary>
/// <param name="userId">The user ID making the request.</param>
/// <param name="language">The programming language for the code generation.</param>
/// <param name="prompt">The code generation prompt.</param>
/// <param name="timestamp">When the request was made.</param>
/// <returns>The queue position or identifier.</returns>
Task<ulong> EnqueueCopilotRequestAsync(ulong userId, string language, string prompt, DateTime timestamp);

/// <summary>
/// Dequeues the next GitHub Copilot request for processing.
/// </summary>
/// <returns>The next copilot request, or null if queue is empty.</returns>
Task<CopilotRequest?> DequeueCopilotRequestAsync();

/// <summary>
/// Gets all pending GitHub Copilot requests for a specific user.
/// </summary>
/// <param name="userId">The user ID.</param>
/// <returns>List of pending requests for the user.</returns>
Task<IReadOnlyList<CopilotRequest>> GetUserPendingRequestsAsync(ulong userId);

/// <summary>
/// Gets the queue position for a specific request.
/// </summary>
/// <param name="requestId">The request identifier.</param>
/// <returns>The position in queue (0-based), or -1 if not found.</returns>
Task<int> GetQueuePositionAsync(ulong requestId);

/// <summary>
/// Marks a request as completed.
/// </summary>
/// <param name="requestId">The request identifier.</param>
/// <param name="result">The generated code result.</param>
/// <returns>True if successfully marked as completed.</returns>
Task<bool> CompleteRequestAsync(ulong requestId, string result);

/// <summary>
/// Gets the total number of pending requests in the queue.
/// </summary>
/// <returns>Number of pending requests.</returns>
Task<int> GetQueueLengthAsync();

/// <summary>
/// Cleans up old completed requests older than the specified timespan.
/// </summary>
/// <param name="maxAge">Maximum age for keeping completed requests.</param>
/// <returns>Number of requests cleaned up.</returns>
Task<int> CleanupOldRequestsAsync(TimeSpan maxAge);
}

/// <summary>
/// Represents a GitHub Copilot code generation request.
/// </summary>
public class CopilotRequest
{
public ulong RequestId { get; set; }
public ulong UserId { get; set; }
public string Language { get; set; } = string.Empty;
public string Prompt { get; set; } = string.Empty;
public DateTime Timestamp { get; set; }
public CopilotRequestStatus Status { get; set; }
public string? Result { get; set; }
public DateTime? CompletedAt { get; set; }
}

/// <summary>
/// Status of a GitHub Copilot request.
/// </summary>
public enum CopilotRequestStatus
{
Pending,
Processing,
Completed,
Failed
}
}
1 change: 0 additions & 1 deletion csharp/Interfaces/ITracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Threading;
using System.Threading.Tasks;
using Octokit;
using Storage.Remote.GitHub;

namespace Interfaces
{
Expand Down
6 changes: 1 addition & 5 deletions csharp/Interfaces/Interfaces.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Storage\Storage.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Platform.Data.Doublets.Sequences" Version="0.1.1" />
<PackageReference Include="Octokit" Version="7.0.1" />
</ItemGroup>

</Project>
202 changes: 202 additions & 0 deletions csharp/Platform.Bot/CopilotIntegration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using System;
using System.Threading.Tasks;
using Bot.Interfaces;
using Storage;

namespace Platform.Bot
{
/// <summary>
/// Integration class for GitHub Copilot functionality in the bot.
/// </summary>
public class CopilotIntegration
{
private readonly IDataService _dataService;
private readonly CopilotQueueManager _queueManager;

/// <summary>
/// Initializes a new instance of the CopilotIntegration.
/// </summary>
/// <param name="dataService">The data service for managing requests.</param>
/// <param name="queueManager">The queue manager for processing requests.</param>
public CopilotIntegration(IDataService dataService, CopilotQueueManager queueManager)
{
_dataService = dataService ?? throw new ArgumentNullException(nameof(dataService));
_queueManager = queueManager ?? throw new ArgumentNullException(nameof(queueManager));
}

/// <summary>
/// Handles a Copilot request from a user (e.g., from Discord, VK, or other chat platforms).
/// </summary>
/// <param name="userId">The user ID making the request.</param>
/// <param name="language">The programming language.</param>
/// <param name="prompt">The code generation prompt.</param>
/// <returns>A message indicating the request status and queue position.</returns>
public async Task<string> HandleCopilotRequestAsync(ulong userId, string language, string prompt)
{
try
{
// Check if user has too many pending requests
var userPendingRequests = await _dataService.GetUserPendingRequestsAsync(userId);
if (userPendingRequests.Count >= 3) // Limit to 3 pending requests per user
{
return $"You already have {userPendingRequests.Count} pending requests. Please wait for them to complete before submitting new ones.";
}

// Enqueue the request
var requestId = await _dataService.EnqueueCopilotRequestAsync(userId, language, prompt, DateTime.UtcNow);

// Get queue position
var position = await _dataService.GetQueuePositionAsync(requestId);
var queueLength = await _dataService.GetQueueLengthAsync();

if (position == 0)
{
return $"โœ… Your {language} code generation request has been queued and will be processed shortly. Request ID: {requestId}";
}
else
{
return $"โœ… Your {language} code generation request has been queued. You are #{position + 1} in line out of {queueLength} requests. Request ID: {requestId}";
}
}
catch (Exception ex)
{
return $"โŒ Error processing your request: {ex.Message}";
}
}

/// <summary>
/// Gets the status of a specific request.
/// </summary>
/// <param name="userId">The user ID.</param>
/// <param name="requestId">The request ID.</param>
/// <returns>Status message for the request.</returns>
public async Task<string> GetRequestStatusAsync(ulong userId, ulong requestId)
{
try
{
var position = await _dataService.GetQueuePositionAsync(requestId);

if (position == -1)
{
return "Request not found or already completed.";
}
else if (position == 0)
{
return $"Your request #{requestId} is currently being processed.";
}
else
{
return $"Your request #{requestId} is #{position + 1} in the queue.";
}
}
catch (Exception ex)
{
return $"โŒ Error checking request status: {ex.Message}";
}
}

/// <summary>
/// Gets all pending requests for a user.
/// </summary>
/// <param name="userId">The user ID.</param>
/// <returns>Summary of user's pending requests.</returns>
public async Task<string> GetUserPendingRequestsAsync(ulong userId)
{
try
{
var requests = await _dataService.GetUserPendingRequestsAsync(userId);

if (requests.Count == 0)
{
return "You have no pending Copilot requests.";
}

var response = $"๐Ÿ“‹ You have {requests.Count} pending request(s):\n";
foreach (var request in requests)
{
var position = await _dataService.GetQueuePositionAsync(request.RequestId);
var status = position == -1 ? "Processing" : $"#{position + 1} in queue";
response += $"โ€ข Request #{request.RequestId}: {request.Language} - {status}\n";
}

return response.TrimEnd();
}
catch (Exception ex)
{
return $"โŒ Error retrieving your requests: {ex.Message}";
}
}

/// <summary>
/// Gets general queue statistics.
/// </summary>
/// <returns>Queue statistics message.</returns>
public async Task<string> GetQueueStatsAsync()
{
try
{
var stats = await _queueManager.GetStatisticsAsync();

return $"""
๐Ÿ“Š **Copilot Queue Statistics**
โ€ข Pending requests: {stats.PendingRequests}
โ€ข Processing interval: {stats.ProcessingInterval.TotalSeconds}s
โ€ข Currently processing: {(stats.IsProcessing ? "Yes" : "No")}
โ€ข Last check: {stats.LastProcessedAt:HH:mm:ss}
""";
}
catch (Exception ex)
{
return $"โŒ Error retrieving queue statistics: {ex.Message}";
}
}

/// <summary>
/// Validates if the specified programming language is supported.
/// </summary>
/// <param name="language">The programming language to validate.</param>
/// <returns>True if supported, false otherwise.</returns>
public static bool IsSupportedLanguage(string language)
{
var supportedLanguages = new[]
{
"python", "py",
"javascript", "js",
"typescript", "ts",
"csharp", "c#", "cs",
"java",
"go",
"rust", "rs",
"cpp", "c++",
"c",
"php",
"ruby", "rb",
"kotlin", "kt"
};

return Array.Exists(supportedLanguages, lang =>
string.Equals(lang, language, StringComparison.OrdinalIgnoreCase));
}

/// <summary>
/// Normalizes the programming language name for consistency.
/// </summary>
/// <param name="language">The input language name.</param>
/// <returns>Normalized language name.</returns>
public static string NormalizeLanguage(string language)
{
return language.ToLowerInvariant() switch
{
"py" => "python",
"js" => "javascript",
"ts" => "typescript",
"cs" or "c#" => "csharp",
"rs" => "rust",
"rb" => "ruby",
"kt" => "kotlin",
"c++" => "cpp",
_ => language.ToLowerInvariant()
};
}
}
}
Loading
Loading