Files
GmRelayBot/docs/c4-system-context.md
T
Toutsu 0c1d3abd7e
PR Checks / test-and-build (pull_request) Successful in 12m32s
feat(web): add public master profiles
Add sanitized public GM profiles with publication controls, public /gm/{slug} pages, and links from public game surfaces.

Bump version -> 3.5.0
2026-05-29 00:08:14 +03:00

7.1 KiB

GM-Relay - C4 Model

Level 1: System Context

C4Context
    title GM-Relay System Context

    Person(gm, "Game Master", "Creates sessions and manages schedules")
    Person(player, "Player", "Joins, leaves, confirms, and receives reminders")
    Person(visitor, "Public visitor", "Views published club schedules, sessions, and GM profiles without private player data")

    System(gmrelay, "GM-Relay", "Telegram bot, Discord worker, web dashboard, public club/session/GM profile pages, and shared scheduling logic")

    System_Ext(telegram, "Telegram Bot API", "Commands, inline keyboards, callback queries, Mini App entry points")
    System_Ext(discord, "Discord Gateway and REST API", "Slash commands, button interactions, message edits, ephemeral replies")
    SystemDb_Ext(postgres, "PostgreSQL", "Sessions, players, participants, groups, platform identities, sanitized master_profiles")

    Rel(gm, telegram, "Creates and manages sessions")
    Rel(gm, discord, "Uses /newsession and /listsessions")
    Rel(player, telegram, "Uses inline buttons")
    Rel(player, discord, "Uses Join/Leave and RSVP buttons")
    Rel(visitor, gmrelay, "Views public club, session, and GM profile pages")
    Rel(telegram, gmrelay, "Updates via long polling")
    Rel(discord, gmrelay, "Gateway events and component interactions")
    Rel(gmrelay, telegram, "SendMessage, EditMessage, AnswerCallbackQuery")
    Rel(gmrelay, discord, "Send/edit schedule, RSVP, reminder, and reschedule messages")
    Rel(gmrelay, postgres, "SQL via Npgsql and Dapper")

Level 2: Container

C4Container
    title GM-Relay Containers

    Person(gm, "Game Master")
    Person(player, "Player")
    Person(visitor, "Public visitor")

    System_Boundary(runtime, "Docker Compose / Aspire runtime") {
        Container(bot, "GmRelay.Bot", "Worker Service, .NET 10 AOT", "Telegram long polling, commands, callback routing, reminders")
        Container(discordBot, "Discord Gateway Worker", "Внутри GmRelay.Bot", "NetCord Gateway, slash commands, scheduler notifications, button interactions, healthcheck :8082")
        Container(web, "GmRelay.Web", "Blazor Server", "Dashboard, Mini App pages, public club/session/GM profile pages, editing and stats")
        Container(shared, "GmRelay.Shared", ".NET library", "Shared domain models, rendering, scheduler, and platform-neutral handlers")
        ContainerDb(db, "PostgreSQL", "Database", "sessions, players, session_participants, game_groups, publication settings, master_profiles, platform identities")
    }

    System_Ext(telegram, "Telegram Bot API")
    System_Ext(discord, "Discord Gateway and REST API")

    Rel(gm, telegram, "Commands")
    Rel(gm, discord, "Slash commands")
    Rel(player, telegram, "Callback queries")
    Rel(player, discord, "Button interactions")
    Rel(visitor, web, "Read-only public schedule and sanitized GM profile pages")
    Rel(telegram, bot, "GetUpdates")
    Rel(discord, discordBot, "Gateway events")
    Rel(bot, telegram, "Bot API calls")
    Rel(discordBot, discord, "REST send/edit/reply calls")
    Rel(bot, shared, "Uses shared renderers and join/leave handlers")
    Rel(discordBot, shared, "Uses shared renderers, scheduler, and platform-neutral handlers")
    Rel(web, shared, "Uses shared domain and rendering models")
    Rel(bot, db, "Npgsql + Dapper.AOT")
    Rel(discordBot, db, "Npgsql + Dapper")
    Rel(web, db, "Npgsql + Dapper")

Level 3: Component - Session Interactions

C4Component
    title Platform-Neutral Session Interactions

    Container_Boundary(shared, "GmRelay.Shared") {
        Component(join, "JoinSessionHandler", "Feature handler", "Adds players as Active or Waitlisted with session row locking")
        Component(leave, "LeaveSessionHandler", "Feature handler", "Removes players and promotes the first waitlisted player when capacity allows")
        Component(rsvp, "HandleRsvpHandler", "Feature handler", "Updates RSVP state and emits platform-neutral RSVP outcomes")
        Component(scheduler, "SessionSchedulerService", "Background service", "Triggers confirmation, reminder, and join-link notifications per platform")
        Component(updateLock, "ScheduleMessageUpdateLock", "In-memory keyed lock", "Serializes DB changes and schedule message edits per platform message")
        Component(renderer, "SessionBatchViewBuilder", "Renderer model builder", "Builds platform-neutral schedule views and actions")
    }

    Component(healthCheck, "DiscordHealthCheckHostedService", ":8082", "Healthcheck для Docker Compose")

    Container_Boundary(discordBot, "GmRelay.DiscordBot") {
        Component(discordModule, "DiscordSessionInteractionModule", "NetCord component module", "Maps join_session/leave_session/rsvp buttons to neutral commands")
        Component(discordMessenger, "DiscordPlatformMessenger", "IPlatformMessenger", "Sends and edits Discord schedule, RSVP, reminder, join-link, and reschedule messages")
    }

    Container_Boundary(bot, "GmRelay.Bot") {
        Component(updateRouter, "UpdateRouter", "Telegram adapter", "Maps callback queries to neutral commands")
        Component(telegramMessenger, "TelegramPlatformMessenger", "IPlatformMessenger", "Sends and edits Telegram schedule, RSVP, reminder, join-link, and reschedule messages")
    }

    ContainerDb(db, "PostgreSQL")
    System_Ext(telegram, "Telegram Bot API")
    System_Ext(discord, "Discord Gateway and REST API")

    Rel(discord, discordModule, "Button interaction")
    Rel(discordModule, join, "JoinSessionCommand")
    Rel(discordModule, leave, "LeaveSessionCommand")
    Rel(discordModule, rsvp, "HandleRsvpCommand")
    Rel(discordModule, discord, "Deferred ephemeral reply, then modify response")
    Rel(updateRouter, join, "JoinSessionCommand")
    Rel(updateRouter, leave, "LeaveSessionCommand")
    Rel(updateRouter, rsvp, "HandleRsvpCommand")
    Rel(join, updateLock, "Acquire by PlatformMessageRef")
    Rel(leave, updateLock, "Acquire by PlatformMessageRef")
    Rel(join, db, "SELECT FOR UPDATE, INSERT participant")
    Rel(leave, db, "SELECT FOR UPDATE, DELETE/promote participant")
    Rel(rsvp, db, "Update RSVP and load notification recipients")
    Rel(scheduler, db, "Load due session triggers")
    Rel(join, renderer, "Build updated schedule view")
    Rel(leave, renderer, "Build updated schedule view")
    Rel(join, discordMessenger, "Update Discord schedule when command is Discord")
    Rel(leave, discordMessenger, "Update Discord schedule when command is Discord")
    Rel(join, telegramMessenger, "Update Telegram schedule when command is Telegram")
    Rel(leave, telegramMessenger, "Update Telegram schedule when command is Telegram")
    Rel(rsvp, discordMessenger, "Update Discord confirmation and outcomes")
    Rel(rsvp, telegramMessenger, "Update Telegram confirmation and outcomes")
    Rel(scheduler, discordMessenger, "Send Discord scheduler notifications")
    Rel(scheduler, telegramMessenger, "Send Telegram scheduler notifications")
    Rel(discordMessenger, discord, "REST send/edit/DM + ephemeral text")
    Rel(telegramMessenger, telegram, "SendMessage/EditMessage + AnswerCallbackQuery")
    Rel(healthCheck, discord, "HTTP /health")