78 lines
3.1 KiB
C#
78 lines
3.1 KiB
C#
using System.Text;
|
|
using Dapper;
|
|
using GmRelay.Shared.Domain;
|
|
using Npgsql;
|
|
using Telegram.Bot;
|
|
using Telegram.Bot.Types;
|
|
|
|
namespace GmRelay.Bot.Features.Sessions.ExportCalendar;
|
|
|
|
internal sealed record CalendarSessionDto(Guid Id, string Title, DateTime ScheduledAt);
|
|
|
|
public sealed class ExportCalendarHandler(
|
|
NpgsqlDataSource dataSource,
|
|
ITelegramBotClient botClient)
|
|
{
|
|
public async Task HandleAsync(Message message, CancellationToken cancellationToken)
|
|
{
|
|
await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);
|
|
|
|
var sessions = await connection.QueryAsync<CalendarSessionDto>(
|
|
@"SELECT s.id as Id, s.title as Title, s.scheduled_at as ScheduledAt
|
|
FROM sessions s
|
|
JOIN game_groups g ON s.group_id = g.id
|
|
WHERE g.telegram_chat_id = @ChatId
|
|
AND s.status = @Planned
|
|
AND s.scheduled_at > NOW()
|
|
ORDER BY s.scheduled_at ASC",
|
|
new { ChatId = message.Chat.Id, Planned = SessionStatus.Planned });
|
|
|
|
var sessionsList = sessions.ToList();
|
|
|
|
if (sessionsList.Count == 0)
|
|
{
|
|
await botClient.SendMessage(
|
|
chatId: message.Chat.Id,
|
|
text: "📭 У этой группы нет запланированных сессий для экспорта.",
|
|
cancellationToken: cancellationToken);
|
|
return;
|
|
}
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("BEGIN:VCALENDAR");
|
|
sb.AppendLine("VERSION:2.0");
|
|
sb.AppendLine("PRODID:-//GM-Relay//TTRPG Schedule//EN");
|
|
|
|
foreach (var s in sessionsList)
|
|
{
|
|
var dtStart = s.ScheduledAt.ToString("yyyyMMddTHHmmssZ");
|
|
var dtEnd = s.ScheduledAt.AddHours(4).ToString("yyyyMMddTHHmmssZ");
|
|
|
|
sb.AppendLine("BEGIN:VEVENT");
|
|
sb.AppendLine($"UID:{s.Id}@gmrelay");
|
|
sb.AppendLine($"DTSTAMP:{DateTime.UtcNow:yyyyMMddTHHmmssZ}");
|
|
sb.AppendLine($"DTSTART:{dtStart}");
|
|
sb.AppendLine($"DTEND:{dtEnd}");
|
|
sb.AppendLine($"SUMMARY:{s.Title}");
|
|
// Escape special chars according to iCal standards (RFC 5545) -- simple escaping for summary
|
|
// In a fuller implementation we'd escape \r\n, commas, etc. But titles are mostly plain text.
|
|
sb.AppendLine("END:VEVENT");
|
|
}
|
|
|
|
sb.AppendLine("END:VCALENDAR");
|
|
|
|
var bytes = Encoding.UTF8.GetBytes(sb.ToString());
|
|
using var stream = new MemoryStream(bytes);
|
|
|
|
var inputFile = InputFile.FromStream(stream, "schedule.ics");
|
|
|
|
await botClient.SendDocument(
|
|
chatId: message.Chat.Id,
|
|
document: inputFile,
|
|
caption: "📅 <b>Ваш календарь игр!</b>\nОткройте файл на устройстве, чтобы добавить события в свой календарь.",
|
|
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
|
|
messageThreadId: message.MessageThreadId,
|
|
cancellationToken: cancellationToken);
|
|
}
|
|
}
|