124 lines
5.2 KiB
C#
124 lines
5.2 KiB
C#
using Dapper;
|
|
using Npgsql;
|
|
using Telegram.Bot;
|
|
using GmRelay.Shared.Domain;
|
|
|
|
namespace GmRelay.Bot.Features.Sessions.ListSessions;
|
|
|
|
public sealed record DeleteSessionCommand(
|
|
Guid SessionId,
|
|
long TelegramUserId,
|
|
string CallbackQueryId,
|
|
long ChatId,
|
|
int MessageId);
|
|
|
|
internal sealed record DeleteSessionInfoDto(string Title, Guid BatchId, long GmId, int? ThreadId);
|
|
|
|
public sealed class DeleteSessionHandler(
|
|
NpgsqlDataSource dataSource,
|
|
ITelegramBotClient bot,
|
|
ILogger<DeleteSessionHandler> logger)
|
|
{
|
|
public async Task HandleAsync(DeleteSessionCommand command, CancellationToken ct)
|
|
{
|
|
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
|
await using var transaction = await connection.BeginTransactionAsync(ct);
|
|
|
|
// 1. Fetch session and verify GM
|
|
var session = await connection.QuerySingleOrDefaultAsync<DeleteSessionInfoDto>(
|
|
@"SELECT s.title as Title, s.batch_id as BatchId, s.thread_id as ThreadId, g.gm_telegram_id as GmId
|
|
FROM sessions s
|
|
JOIN game_groups g ON s.group_id = g.id
|
|
WHERE s.id = @SessionId",
|
|
new { command.SessionId }, transaction);
|
|
|
|
if (session == null)
|
|
{
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия не найдена.", cancellationToken: ct);
|
|
return;
|
|
}
|
|
|
|
if (session.GmId != command.TelegramUserId)
|
|
{
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Только Мастер Игры (GM) может удалять сессию.", showAlert: true, cancellationToken: ct);
|
|
return;
|
|
}
|
|
|
|
// 2. Delete session
|
|
await connection.ExecuteAsync("DELETE FROM sessions WHERE id = @Id", new { Id = command.SessionId }, transaction);
|
|
|
|
// 3. Check if any sessions are left in the batch
|
|
var remainingInBatch = await connection.ExecuteScalarAsync<int>(
|
|
"SELECT COUNT(*) FROM sessions WHERE batch_id = @BatchId",
|
|
new { BatchId = session.BatchId }, transaction);
|
|
|
|
await transaction.CommitAsync(ct);
|
|
|
|
// 4. If no sessions left and we have a forum topic, delete the topic
|
|
if (remainingInBatch == 0 && session.ThreadId.HasValue)
|
|
{
|
|
try
|
|
{
|
|
await bot.DeleteForumTopic(command.ChatId, session.ThreadId.Value, cancellationToken: ct);
|
|
logger.LogInformation("Deleted forum topic {ThreadId} for batch {BatchId} as no sessions remained.", session.ThreadId.Value, session.BatchId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning(ex, "Failed to delete forum topic {ThreadId}", session.ThreadId.Value);
|
|
}
|
|
}
|
|
|
|
await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия удалена!", cancellationToken: ct);
|
|
|
|
// 5. Update the /listsessions message (we delete the message or edit it to remove the button)
|
|
// A simple way is to re-render the list:
|
|
await using var readConnection = await dataSource.OpenConnectionAsync(ct);
|
|
var sessions = await readConnection.QueryAsync<SessionListItemDto>(
|
|
@"SELECT s.id as Id, s.title as Title, s.scheduled_at as ScheduledAt, s.status as Status,
|
|
COUNT(sp.id) FILTER (WHERE sp.is_gm = false) as PlayerCount,
|
|
g.gm_telegram_id as GmId
|
|
FROM sessions s
|
|
JOIN game_groups g ON s.group_id = g.id
|
|
LEFT JOIN session_participants sp ON s.id = sp.session_id
|
|
WHERE g.telegram_chat_id = @ChatId AND s.status != @Cancelled AND s.scheduled_at > NOW()
|
|
GROUP BY s.id, s.title, s.scheduled_at, s.status, g.gm_telegram_id
|
|
ORDER BY s.scheduled_at ASC",
|
|
new { ChatId = command.ChatId, Cancelled = SessionStatus.Cancelled });
|
|
|
|
var sessionsList = sessions.ToList();
|
|
|
|
if (sessionsList.Count == 0)
|
|
{
|
|
try { await bot.EditMessageText(command.ChatId, command.MessageId, "📭 В этой группе нет предстоящих игр.", cancellationToken: ct); } catch {}
|
|
return;
|
|
}
|
|
|
|
var text = "📅 <b>Ближайшие игры:</b>\n\n";
|
|
foreach (var s in sessionsList)
|
|
{
|
|
text += $"🔹 <b>{s.ScheduledAt.FormatMoscow()}</b> — {System.Net.WebUtility.HtmlEncode(s.Title)} (Участников: {s.PlayerCount})\n";
|
|
}
|
|
|
|
var isGm = command.TelegramUserId == sessionsList.First().GmId;
|
|
var keyboard = isGm
|
|
? new Telegram.Bot.Types.ReplyMarkups.InlineKeyboardMarkup(
|
|
sessionsList.Select(s => new[] { Telegram.Bot.Types.ReplyMarkups.InlineKeyboardButton.WithCallbackData($"🗑 Удалить {s.ScheduledAt.FormatMoscowShort()}", $"delete_session:{s.Id}") }))
|
|
: null;
|
|
|
|
try
|
|
{
|
|
await bot.EditMessageText(
|
|
command.ChatId,
|
|
command.MessageId,
|
|
text,
|
|
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
|
|
replyMarkup: keyboard,
|
|
cancellationToken: ct);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning(ex, "Failed to edit list sessions message");
|
|
}
|
|
}
|
|
}
|