988133e389
Deploy Telegram Bot / deploy (push) Has been cancelled
- Created GmRelay.Web project (Blazor Server) - Created GmRelay.Shared library for domain models and rendering - Refactored GmRelay.Bot to use the Shared library - Integrated Telegram Login widget with server-side HMAC verification - Added Dashboard, Group Details, and Edit Session pages - Enabled bot notifications and in-place message updates from web actions - Updated .NET Aspire orchestration and Docker Compose configuration
101 lines
4.6 KiB
C#
101 lines
4.6 KiB
C#
using Dapper;
|
|
using Npgsql;
|
|
using Telegram.Bot;
|
|
using Telegram.Bot.Types;
|
|
using Telegram.Bot.Types.ReplyMarkups;
|
|
using GmRelay.Shared.Domain;
|
|
using GmRelay.Shared.Rendering;
|
|
|
|
namespace GmRelay.Bot.Features.Sessions.CreateSession;
|
|
|
|
public sealed record JoinSessionCommand(
|
|
Guid SessionId,
|
|
long TelegramUserId,
|
|
string DisplayName,
|
|
string? TelegramUsername,
|
|
string CallbackQueryId,
|
|
long ChatId,
|
|
int MessageId);
|
|
|
|
// DTOs for AOT compilation
|
|
internal sealed record JoinSessionBatchDto(Guid BatchId, string Title);
|
|
|
|
public sealed class JoinSessionHandler(
|
|
NpgsqlDataSource dataSource,
|
|
ITelegramBotClient bot,
|
|
ILogger<JoinSessionHandler> logger)
|
|
{
|
|
public async Task HandleAsync(JoinSessionCommand command, CancellationToken ct)
|
|
{
|
|
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
|
await using var transaction = await connection.BeginTransactionAsync(ct);
|
|
|
|
try
|
|
{
|
|
// 1. Убеждаемся, что игрок есть в базе
|
|
var playerId = await connection.ExecuteScalarAsync<Guid>(
|
|
@"INSERT INTO players (telegram_id, display_name, telegram_username)
|
|
VALUES (@TgId, @Name, @Username)
|
|
ON CONFLICT (telegram_id) DO UPDATE SET display_name = EXCLUDED.display_name, telegram_username = EXCLUDED.telegram_username
|
|
RETURNING id;",
|
|
new { TgId = command.TelegramUserId, Name = command.DisplayName, Username = command.TelegramUsername },
|
|
transaction);
|
|
|
|
// 2. Добавляем в участники сессии (статус Pending, так как за 24 часа нужно будет финальное подтверждение)
|
|
var inserted = await connection.ExecuteAsync(
|
|
@"INSERT INTO session_participants (session_id, player_id, is_gm, rsvp_status)
|
|
VALUES (@SessionId, @PlayerId, false, 'Pending')
|
|
ON CONFLICT (session_id, player_id) DO NOTHING;",
|
|
new { SessionId = command.SessionId, PlayerId = playerId },
|
|
transaction);
|
|
|
|
if (inserted == 0)
|
|
{
|
|
await transaction.RollbackAsync(ct);
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Вы уже записаны!", cancellationToken: ct);
|
|
return;
|
|
}
|
|
|
|
// 3. Получаем batch_id по session_id
|
|
var batchInfo = await connection.QuerySingleAsync<JoinSessionBatchDto>(
|
|
@"SELECT batch_id as BatchId, title as Title FROM sessions WHERE id = @SessionId",
|
|
new { command.SessionId }, transaction);
|
|
|
|
// Загружаем весь батч для перерисовки
|
|
var batchSessions = await connection.QueryAsync<SessionBatchDto>(
|
|
@"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
|
new { BatchId = batchInfo.BatchId }, transaction);
|
|
|
|
var batchParticipants = await connection.QueryAsync<ParticipantBatchDto>(
|
|
@"SELECT sp.session_id as SessionId, p.display_name as DisplayName, p.telegram_username as TelegramUsername
|
|
FROM session_participants sp
|
|
JOIN players p ON sp.player_id = p.id
|
|
JOIN sessions s ON sp.session_id = s.id
|
|
WHERE s.batch_id = @BatchId AND sp.is_gm = false
|
|
ORDER BY sp.responded_at ASC, p.created_at ASC",
|
|
new { BatchId = batchInfo.BatchId }, transaction);
|
|
|
|
await transaction.CommitAsync(ct);
|
|
|
|
// 4. Перерисовываем сообщение
|
|
var renderResult = SessionBatchRenderer.Render(batchInfo.Title, batchSessions.ToList(), batchParticipants.ToList());
|
|
|
|
await bot.EditMessageText(
|
|
chatId: command.ChatId,
|
|
messageId: command.MessageId,
|
|
text: renderResult.Text,
|
|
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
|
|
replyMarkup: renderResult.Markup,
|
|
cancellationToken: ct);
|
|
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Вы успешно записаны!", cancellationToken: ct);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Ошибка при добавлении игрока к сессии");
|
|
await transaction.RollbackAsync(ct);
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Произошла ошибка при регистрации.", cancellationToken: ct);
|
|
}
|
|
}
|
|
}
|