Skip to content

Main#4

Merged
HarshBhanushali07 merged 2 commits into
masterfrom
main
Mar 19, 2026
Merged

Main#4
HarshBhanushali07 merged 2 commits into
masterfrom
main

Conversation

@HarshBhanushali07
Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings March 19, 2026 20:29
@HarshBhanushali07 HarshBhanushali07 merged commit 2c78dfa into master Mar 19, 2026
5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Telegram bot integration to DualMind.API (long-polling hosted service, command handlers, auth/session persistence), expands AI provider support via Cloudflare AI Gateway + Cloudflare Workers AI, and updates CI/deployment configuration to target the main branch.

Changes:

  • Add Telegram bot background service, transport abstraction, command/update handling, session storage, and supporting unit tests.
  • Add Cloudflare AI Gateway settings + Cloudflare Workers AI provider; adjust model selection/provider factory behavior accordingly.
  • Add/adjust Supabase single-row select behavior and introduce new DB migration scripts for Workers AI models + leaderboard seeding.

Reviewed changes

Copilot reviewed 68 out of 69 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/DualMind.API.Tests/UnitTest1.cs Removes placeholder test.
tests/DualMind.API.Tests/TelegramUpdateHandlerTests.cs Adds bot update handler coverage (start/help/sign-in flow).
tests/DualMind.API.Tests/TelegramStateCacheTests.cs Adds state/cooldown/vote tracking tests.
tests/DualMind.API.Tests/TelegramSessionStoreTests.cs Adds encryption round-trip test for Telegram sessions.
tests/DualMind.API.Tests/TelegramBotServiceCollectionExtensionsTests.cs Adds DI/config gating tests for bot registration.
tests/DualMind.API.Tests/TelegramAuthServiceTests.cs Adds session persistence/refresh/legacy-session behavior tests.
tests/DualMind.API.Tests/SupabaseServiceTests.cs Adds coverage for PostgREST “0 rows” 406 behavior handling.
tests/DualMind.API.Tests/StatsCommandHandlerTests.cs Adds leaderboard rendering test for stats command.
tests/DualMind.API.Tests/ModelSelectorTests.cs Adds filtering/exclusion tests for provider/model selection.
tests/DualMind.API.Tests/CloudflareAiGatewaySettingsTests.cs Adds tests for gateway URL/model mapping and validation.
tests/DualMind.API.Tests/BotTestDoubles.cs Adds fakes/stubs used by bot tests (transport, auth, supabase, time).
tests/DualMind.API.Tests/BattleCommandHandlerTests.cs Adds battle/vote/retry/cooldown/timeout tests.
src/DualMind.API/appsettings.json Adds Telegram config defaults; updates Supabase settings.
src/DualMind.API/appsettings.Development.json Adds Telegram config defaults for development.
src/DualMind.API/Program.cs Registers Telegram bot services + adds Cloudflare Workers AI typed client.
src/DualMind.API/Infrastructure/Data/SupabaseService.cs Treats specific PostgREST 406 “0 rows” as default/null for SelectSingle.
src/DualMind.API/Infrastructure/Configuration/EnvConfig.cs Adds env vars for Cloudflare AI Gateway + Workers AI.
src/DualMind.API/Infrastructure/Configuration/CloudflareAiGatewaySettings.cs Introduces Cloudflare gateway config/validation + model mapping helpers.
src/DualMind.API/DualMind.API.csproj Adds Telegram.Bot dependency.
src/DualMind.API/Core/Services/ModelSelector.cs Filters unsupported providers; excludes manual-only providers from auto-selection.
src/DualMind.API/Core/Services/LeaderboardModelSelector.cs Ensures leaderboard selection only uses models present in current catalog.
src/DualMind.API/Bot/Transport/TelegramBotTransport.cs Adds Telegram.Bot-backed transport implementation.
src/DualMind.API/Bot/Transport/ITelegramBotTransport.cs Adds transport abstraction for bot messaging/updates.
src/DualMind.API/Bot/TelegramUpdateHandler.cs Adds command parsing + callback/message routing + sign-in flow.
src/DualMind.API/Bot/TelegramStateCache.cs Adds per-chat state/session caching + cooldown/vote tracking.
src/DualMind.API/Bot/TelegramSessionStore.cs Adds Supabase-backed encrypted Telegram session persistence.
src/DualMind.API/Bot/TelegramMessageFormatter.cs Adds bot message formatting + keyboards + markdown escaping helper.
src/DualMind.API/Bot/TelegramBotServiceCollectionExtensions.cs Adds opt-in Telegram bot DI registration based on config presence.
src/DualMind.API/Bot/TelegramBotService.cs Adds long-polling hosted service + bot command registration.
src/DualMind.API/Bot/TelegramBotOptions.cs Adds Telegram bot configuration options and enablement check.
src/DualMind.API/Bot/TelegramBotExceptions.cs Adds bot/auth/API exception types.
src/DualMind.API/Bot/TelegramAuthService.cs Adds session validation/refresh/sign-in with per-chat async locking.
src/DualMind.API/Bot/SupabaseTelegramAuthClient.cs Adds Supabase password/refresh token client for bot auth.
src/DualMind.API/Bot/Models/UserState.cs Adds user state model + modes.
src/DualMind.API/Bot/Models/TelegramBotCommand.cs Adds bot command model.
src/DualMind.API/Bot/Models/BattleSession.cs Adds in-memory battle session + vote-state handling.
src/DualMind.API/Bot/Models/ApiResponseModels.cs Adds API DTOs for bot and transport update/message models.
src/DualMind.API/Bot/ITelegramSessionStore.cs Adds session store interface.
src/DualMind.API/Bot/ITelegramAuthService.cs Adds auth service interface.
src/DualMind.API/Bot/ISupabaseTelegramAuthClient.cs Adds Supabase auth client interface.
src/DualMind.API/Bot/IDualMindBotApiClient.cs Adds bot API client interface for arena endpoints.
src/DualMind.API/Bot/DualMindBotApiClient.cs Adds HTTP client for battle/vote/stats endpoints.
src/DualMind.API/Bot/Commands/StatsCommandHandler.cs Adds stats command handler.
src/DualMind.API/Bot/Commands/StartCommandHandler.cs Adds start command handler.
src/DualMind.API/Bot/Commands/HelpCommandHandler.cs Adds help command handler.
src/DualMind.API/Bot/Commands/CancelCommandHandler.cs Adds cancel command handler.
src/DualMind.API/Bot/Commands/BattleCommandHandler.cs Adds battle prompt flow + vote submission + auth retry/cooldown handling.
src/DualMind.API/AI/Providers/GroqService.cs Routes chat/streaming via Cloudflare AI Gateway; keeps speech direct.
src/DualMind.API/AI/Providers/GoogleService.cs Routes chat/streaming via Cloudflare AI Gateway.
src/DualMind.API/AI/Providers/CloudflareWorkersAiService.cs Adds native Workers AI provider implementation.
src/DualMind.API/AI/Gateway/ChatProviderFactory.cs Adds provider routing for Cloudflare/workers-ai and google-ai-studio alias.
postman/cloudflare-workers-ai-models-stable.json Adds reference model list (JSON) for Workers AI.
postman/cloudflare-workers-ai-models-stable.csv Adds reference model list (CSV) for Workers AI.
database/migrations/20260319_seed_cloudflare_leaderboard.sql Adds Cloudflare leaderboard seeding script (auto).
database/migrations/20260319_manual_elo_setup_and_seed.sql Adds ELO infra setup + manual model seeding script.
database/migrations/20260319_manual_cloudflare_leaderboard_seed.sql Adds manual Cloudflare leaderboard seeding script.
database/migrations/20260319_fixed_cloudflare_leaderboard_seed.sql Adds revised seeding script with view/round fixes.
database/migrations/20260319_final_cloudflare_leaderboard_seed.sql Adds final seeding script with explicit view drop/recreate.
database/migrations/20260319_elo_rating_setup.sql Adds ELO infra + view + Cloudflare leaderboard init script.
database/migrations/20260319_add_cloudflare_workers_ai_models.sql Seeds providers + Workers AI model catalog into Supabase tables.
database/migrations/20260319_deactivate_unreachable_cloudflare_workers_ai_models.sql Deactivates specific Workers AI models failing smoke tests.
database/migrations/20260315_add_telegram_session_refresh_tokens.sql Adds refresh token/expiry fields for telegram session persistence.
ENV_SETUP.md Updates environment setup docs for gateway + Telegram config.
.gitignore Ignores .codex/.
.github/workflows/verify.yml Updates workflow triggers from master to main.
.github/workflows/dotnet.yml Removes legacy build/deploy workflow.
.github/workflows/deploy-dualmind-arena.yml Updates deploy workflow to main and artifact packaging paths.
.env.example Adds example env file including Telegram configuration keys.
.env Removes committed local env secrets file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +295 to +301
session = await _authService.ForceRefreshSessionAsync(chatId, cancellationToken);
if (session == null)
{
return null;
}

return await _apiClient.StartBattleAsync(session.AccessToken, prompt, cancellationToken);
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StartBattleWithRetryAsync assigns the nullable result of ForceRefreshSessionAsync into the non-nullable TelegramAuthSession session parameter, which triggers nullable warnings and makes it easier to accidentally dereference null in future edits. Prefer storing the refresh result in a separate TelegramAuthSession? refreshed variable (or change the local to nullable) and only continue once it’s proven non-null.

Suggested change
session = await _authService.ForceRefreshSessionAsync(chatId, cancellationToken);
if (session == null)
{
return null;
}
return await _apiClient.StartBattleAsync(session.AccessToken, prompt, cancellationToken);
var refreshed = await _authService.ForceRefreshSessionAsync(chatId, cancellationToken);
if (refreshed == null)
{
return null;
}
return await _apiClient.StartBattleAsync(refreshed.AccessToken, prompt, cancellationToken);

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +19
private readonly ConcurrentDictionary<long, SemaphoreSlim> _locks = new();

Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The per-chat lock dictionary (_locks) will grow monotonically because entries are never evicted/removed. In a long-running bot this can lead to unbounded memory growth. Consider using an expiring cache for locks, removing locks when sessions are cleared, or associating the async lock with the per-chat state so it can be collected when the state is removed.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
-- =============================================================================
-- FINAL REVISED MANUAL CLOUDFLARE MODEL LEADERBOARD SEEDING
-- =============================================================================
-- Fixes: ERROR 42P16 (cannot drop columns from view)
-- Provides: Manual ELO scores and explicit DROP VIEW for clean replacement.
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple Cloudflare leaderboard seeding scripts are being added for the same day (seed/manual/fixed/final variants). If all files in database/migrations are applied automatically, these will run sequentially and can re-create the same view/table repeatedly. Suggest keeping only the intended final migration(s) and deleting superseded drafts to prevent accidental double-application.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,99 @@
-- =============================================================================
-- DUALMIND ELO SYSTEM & CLOUDFARE LEADERBOARD INITIALIZATION
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in header comment: “CLOUDFARE” → “CLOUDFLARE”.

Suggested change
-- DUALMIND ELO SYSTEM & CLOUDFARE LEADERBOARD INITIALIZATION
-- DUALMIND ELO SYSTEM & CLOUDFLARE LEADERBOARD INITIALIZATION

Copilot uses AI. Check for mistakes.
Comment on lines 10 to +18
"Url": "https://calqfzajyidkdzbaswjp.supabase.co",
"Key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNhbHFmemFqeWlka2R6YmFzd2pwIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQyNzMwODMsImV4cCI6MjA3OTg0OTA4M30.ptXyUNCcAhGi9u2kVDHOxSBvQv0W72S5HHqkIFXQS08",
"ServiceKey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNhbHFmemFqeWlka2R6YmFzd2pwIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2NDI3MzA4MywiZXhwIjoyMDc5ODQ5MDgzfQ.bt3MjR2dItU1FT3yRTlNkNhNPRFO5_NBO1lMCqQy1d8"
},
"Telegram": {
"SignupUrl": "https://dualmind.arena/signup",
"BattleCooldownSeconds": 15,
"SoftTimeoutSeconds": 30,
"ApiTimeoutSeconds": 75
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appsettings.json includes what appear to be real Supabase API keys/service keys. These are secrets and should not be committed to the repo; rotate them and move configuration to environment variables / secret manager (keep placeholders only in appsettings*.json).

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +73
public async Task<TelegramSentMessage> SendTextMessageAsync(long chatId, string text, InlineKeyboardMarkup? replyMarkup, CancellationToken cancellationToken)
{
var message = await _client.SendMessage(
chatId: chatId,
text: text,
parseMode: ParseMode.MarkdownV2,
replyMarkup: replyMarkup,
cancellationToken: cancellationToken);

return new TelegramSentMessage
{
ChatId = chatId,
MessageId = message.MessageId,
Text = message.Text
};
}

public async Task EditMessageTextAsync(long chatId, int messageId, string text, InlineKeyboardMarkup? replyMarkup, CancellationToken cancellationToken)
{
await _client.EditMessageText(
chatId: chatId,
messageId: messageId,
text: text,
parseMode: ParseMode.MarkdownV2,
replyMarkup: replyMarkup,
cancellationToken: cancellationToken);
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TelegramBotTransport forces ParseMode.MarkdownV2 for all messages/edits. Many call sites pass plain text that isn't escaped for MarkdownV2 (e.g., strings with '.' / '-' / '!' etc.), which can cause Telegram to reject the request with “can't parse entities”. Consider either (a) defaulting to no parse mode (plain text) and only enabling MarkdownV2 for messages you explicitly format, or (b) adding a transport API that accepts a parse mode and ensuring every message body is properly escaped before sending.

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +100
var top = stats
.OrderBy(s => s.EloRank == 0 ? int.MaxValue : s.EloRank)
.ThenByDescending(s => s.EloScore)
.Take(10)
.Select((stat, index) =>
{
var rank = stat.EloRank > 0 ? stat.EloRank : index + 1;
var name = string.IsNullOrWhiteSpace(stat.DisplayName) ? stat.ModelName : stat.DisplayName;
return $"{rank}) {EscapeMarkdown(name)} | {EscapeMarkdown(stat.ProviderName)} | Elo {stat.EloScore:F0} | Win {stat.WinRate:F1}%";
});

return "Top Models\n\n" + string.Join("\n", top);
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FormatStats builds MarkdownV2 text but interpolates numeric values directly (e.g., {stat.WinRate:F1} yields 64.8) and includes | / ) formatting. If the transport uses MarkdownV2, the . (and other reserved chars) in the interpolated portions can break parsing. Consider escaping the full rendered line (or at least the formatted numeric segments) consistently, or switch stats rendering to plain text parse mode.

Copilot uses AI. Check for mistakes.
Comment on lines +118 to +142
public static string EscapeMarkdown(string text)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}

return text
.Replace("_", "\\_")
.Replace("*", "\\*")
.Replace("[", "\\[")
.Replace("]", "\\]")
.Replace("(", "\\(")
.Replace(")", "\\)")
.Replace("~", "\\~")
.Replace("`", "\\`")
.Replace(">", "\\>")
.Replace("#", "\\#")
.Replace("+", "\\+")
.Replace("-", "\\-")
.Replace("=", "\\=")
.Replace("|", "\\|")
.Replace("{", "\\{")
.Replace("}", "\\}")
.Replace("!", "\\!");
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EscapeMarkdown is intended for Telegram MarkdownV2 escaping but doesn't escape some characters that are treated as special in MarkdownV2 (notably . and the backslash itself). If you keep using MarkdownV2 globally, expand this helper to cover the full reserved set (and ensure escaping order handles \ first) to avoid intermittent “can't parse entities” failures.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants