From 54751db1fa6ef41910f7172a36df3b141019b249 Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:10:48 +0800 Subject: [PATCH 01/10] Add files via upload --- TELEGRAM_LOGIC_SDK_SPEC.md | 1488 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1488 insertions(+) create mode 100644 TELEGRAM_LOGIC_SDK_SPEC.md diff --git a/TELEGRAM_LOGIC_SDK_SPEC.md b/TELEGRAM_LOGIC_SDK_SPEC.md new file mode 100644 index 0000000..b6243ff --- /dev/null +++ b/TELEGRAM_LOGIC_SDK_SPEC.md @@ -0,0 +1,1488 @@ +# TELEGRAM LOGIC SDK +## Architecture Specification + +> **Document ID:** SDK-SPEC-001 +> **Status:** DRAFT +> **Scope:** SDK Core, Logic Modules, Plugin Interface, Workflow Engine, Action Engine, State Engine, Telegram Adapter +> **Design Principle:** Telegram is the platform. This SDK is only the logic. + +--- + +--- + +# PART 1 — ARCHITECTURAL OVERVIEW + +--- + +## 1.1 DESIGN PHILOSOPHY + +The SDK does not own or manage a Telegram bot. +The SDK does not provision bots, manage tokens, or maintain runtime state. +The SDK assumes it is already running for a configured bot. + +The SDK is a **reusable logic engine** that: + +- Receives normalized events from any messaging adapter +- Resolves context before any logic executes +- Routes events to the correct logic module +- Executes workflows composed of discrete steps +- Returns first-class action objects +- Delegates transport execution to the adapter + +This makes the SDK transport-independent except at the adapter boundary. + +--- + +## 1.2 THE CORE ARCHITECTURE + +``` +Telegram Platform + │ + ▼ +┌───────────────────┐ +│ Telegram Adapter │ ◄── only layer that knows about Telegram +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Event Engine │ ◄── normalizes raw updates into internal events +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Context Engine │ ◄── resolves bot, chat, user, session, permissions +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Logic Router │ ◄── matches event+context to a Logic Module +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Logic Module │ ◄── plugin; returns a workflow or direct actions +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Workflow Engine │ ◄── executes multi-step, stateful workflows +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Action Engine │ ◄── validates and dispatches first-class actions +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ State Engine │ ◄── persists domain state, not Telegram state +└────────┬──────────┘ + │ + ▼ +┌───────────────────┐ +│ Telegram Adapter │ ◄── translates actions into Bot API calls +└────────┬──────────┘ + │ + ▼ +Telegram Platform +``` + +--- + +## 1.3 SDK CORE COMPONENTS + +``` +SDK Core +├── Event Engine +├── Context Engine +├── Logic Router +├── Workflow Engine +├── Action Engine +├── State Engine +└── Telegram Adapter +``` + +Everything else is a plugin. + +``` +plugins/ +├── content/ +├── community/ +├── support/ +├── scheduler/ +├── automation/ +├── broadcast/ +├── analytics/ +└── audit/ +``` + +--- + +--- + +# PART 2 — SDK CORE SPECIFICATION + +--- + +## 2.1 TELEGRAM ADAPTER (INGRESS) + +The Adapter is the only component that communicates with Telegram. +On ingress, it receives raw Telegram updates and forwards them inward. +On egress, it receives Action objects and translates them into Bot API calls. + +``` +╔══════════════════════════════╗ +║ TELEGRAM PLATFORM ║ +╚══════════════╦═══════════════╝ + │ + │ Raw Telegram Update + ▼ +┌──────────────────────────────┐ +│ TELEGRAM ADAPTER │ +│ │ +│ Ingress │ +│ ───────────────────────── │ +│ Receive Update │ +│ Strip transport metadata │ +│ Forward raw payload │ +└──────────────┬───────────────┘ + │ + │ Raw Update Payload + ▼ +┌──────────────────────────────┐ +│ EVENT ENGINE │ +└──────────────────────────────┘ +``` + +**Architectural constraint:** + +``` +TELEGRAM ADAPTER (ingress) + │ + └── MUST NOT ──► Logic Router + └── MUST NOT ──► Logic Modules + └── MUST ──► Event Engine +``` + +--- + +## 2.2 EVENT ENGINE + +The Event Engine normalizes any raw update into a typed, platform-independent Internal Event. + +``` +┌──────────────────────────────┐ +│ EVENT ENGINE │ +├──────────────────────────────┤ +│ Input: Raw Update Payload │ +│ Output: InternalEvent │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ InternalEvent │ +├──────────────────────────────────────────────────────┤ +│ event_id : UUID │ +│ event_type : EventType │ +│ source : AdapterSource │ +│ actor_ref : TelegramUserRef │ +│ chat_ref : TelegramChatRef │ +│ message_ref : TelegramMessageRef | null │ +│ payload : EventPayload │ +│ timestamp : ISO8601 │ +│ correlation_id : UUID │ +└──────────────────────────────────────────────────────┘ +``` + +**Event types:** + +``` +EventType +├── COMMAND +├── CALLBACK_QUERY +├── MESSAGE +├── POLL_ANSWER +├── JOIN_REQUEST +├── MEMBER_EVENT +├── SCHEDULED +└── SYSTEM +``` + +**Constraint:** + +``` +Raw Telegram Update + │ + ├── X ──► Logic Router + ├── X ──► Logic Modules + └── ✓ ──► Event Engine only +``` + +--- + +## 2.3 CONTEXT ENGINE + +The Context Engine runs after normalization and before routing. +No logic module ever executes without a resolved context. + +``` +┌──────────────────────────────┐ +│ InternalEvent │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ CONTEXT ENGINE │ +│ │ +│ Resolve: │ +│ ───────────────────────── │ +│ bot │ +│ chat │ +│ user │ +│ permissions │ +│ session / conversation │ +│ locale │ +│ active workflow │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ ResolvedContext │ +├──────────────────────────────────────────────────────┤ +│ bot : BotConfig │ +│ chat : ChatContext │ +│ user : UserContext │ +│ permissions : PermissionSet │ +│ session : Session | null │ +│ locale : Locale │ +│ workflow : ActiveWorkflow | null │ +└──────────────────────────────────────────────────────┘ +``` + +**Resolution order:** + +``` +InternalEvent + │ + ├── 1. Resolve Bot config + ├── 2. Resolve Chat context + ├── 3. Resolve User context + ├── 4. Evaluate Permissions + ├── 5. Load Session / Conversation state + ├── 6. Detect Locale + └── 7. Check for active Workflow + │ + ▼ +ResolvedContext ──► Logic Router +``` + +--- + +## 2.4 LOGIC ROUTER + +The Logic Router receives an InternalEvent and a ResolvedContext, then selects the correct Logic Module. + +``` +┌──────────────────────────────┐ +│ InternalEvent │ +│ ResolvedContext │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ LOGIC ROUTER │ +│ │ +│ 1. Check active workflow │ +│ → resume if exists │ +│ │ +│ 2. Match against modules │ +│ → call module.match() │ +│ │ +│ 3. Select first match │ +│ │ +│ 4. Dispatch to module │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ Logic Module │ +└──────────────────────────────┘ +``` + +**Active workflow priority:** + +``` +ResolvedContext.workflow != null + │ + ▼ +RESUME WORKFLOW ──► Workflow Engine + │ + (skip module matching entirely) +``` + +--- + +## 2.5 LOGIC MODULE INTERFACE + +Every feature is a plugin that implements a single interface. +No module has privileged access to the transport layer. + +```typescript +interface LogicModule { + /** + * Returns true if this module should handle the event. + * Evaluated against the normalized event and resolved context. + */ + match(event: InternalEvent, context: ResolvedContext): boolean + + /** + * Executes the module logic. + * Returns a Workflow, a list of Actions, or null. + */ + execute( + event: InternalEvent, + context: ResolvedContext + ): Workflow | Action[] | null + + /** + * Declares which Action types this module may produce. + * Used by the Action Engine for validation. + */ + actions(): ActionType[] +} +``` + +**Constraint:** + +``` +Logic Module + │ + ├── MUST NOT ──► Telegram Adapter + ├── MUST NOT ──► Bot API + ├── MAY ──► State Engine (read/write domain state) + └── MUST ──► return Actions or Workflow +``` + +--- + +## 2.6 WORKFLOW ENGINE + +A Workflow is a multi-step, stateful execution sequence. +The Workflow Engine drives each step and persists intermediate state. + +``` +┌──────────────────────────────┐ +│ Logic Module │ +│ returns: Workflow │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ WORKFLOW ENGINE │ +│ │ +│ Load workflow definition │ +│ Restore workflow state │ +│ Execute current step │ +│ Evaluate step result │ +│ Advance or terminate │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ WorkflowStep result │ +├──────────────────────────────────────────────────────┤ +│ actions : Action[] │ +│ next_step : StepID | null │ +│ state : WorkflowState │ +└──────────────────────────────────────────────────────┘ +``` + +**Step execution model:** + +``` +Workflow + │ + ├── Step 1 + │ ├── execute() + │ ├── return Action[] + │ └── next: Step 2 + │ + ├── Step 2 + │ ├── WAIT (awaiting user input) + │ ├── event arrives → resume + │ └── next: Step 3 + │ + └── Step 3 + ├── execute() + ├── return Action[] + └── next: null (terminal) +``` + +**Persistence:** + +``` +WORKFLOW STATE + │ + ▼ +STATE ENGINE + │ + ▼ +PERSISTED + +PROCESS RESTART + │ + ▼ +STATE ENGINE + │ + ▼ +NON-TERMINAL WORKFLOWS RESTORED +``` + +--- + +## 2.7 ACTION ENGINE + +Actions are first-class objects. Modules never call Telegram directly. +The Action Engine validates, sequences, and dispatches all actions. + +``` +┌──────────────────────────────┐ +│ Action[] │ +│ (from module or workflow) │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ ACTION ENGINE │ +│ │ +│ 1. Validate action types │ +│ against module.actions() │ +│ │ +│ 2. Check approval gate │ +│ (if action is protected) │ +│ │ +│ 3. Sequence actions │ +│ │ +│ 4. Dispatch to adapter │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ TELEGRAM ADAPTER (egress) │ +└──────────────────────────────┘ +``` + +**Action catalogue:** + +``` +ActionType +├── SendMessage +├── EditMessage +├── DeleteMessage +├── AnswerCallbackQuery +├── AnswerInlineQuery +├── BanMember +├── UnbanMember +├── MuteMember +├── ApproveJoinRequest +├── DeclineJoinRequest +├── PinMessage +├── CreateTopic +├── PublishPost +├── ForwardMessage +├── SendPoll +└── SendFile +``` + +**Action object structure:** + +```typescript +interface Action { + type : ActionType + target_ref : TelegramChatRef | TelegramUserRef | TelegramMessageRef + payload : ActionPayload + requires_approval : boolean + correlation_id : UUID +} +``` + +**Approval gate:** + +``` +Action.requires_approval == true + │ + ▼ +APPROVAL WORKFLOW + │ + ├── APPROVED ──► dispatch to adapter + │ + └── REJECTED ──► cancel, emit AuditRecord +``` + +--- + +## 2.8 STATE ENGINE + +The State Engine stores business domain state. +It does not store Telegram transport state. + +``` +┌──────────────────────────────────────────────────────┐ +│ STATE ENGINE │ +├──────────────────────────────────────────────────────┤ +│ Domain entities (stored) │ +│ ───────────────────────────────────────────────── │ +│ Ticket │ +│ Content │ +│ Broadcast │ +│ Reminder │ +│ Poll │ +│ Workflow │ +│ Session │ +│ AuditRecord │ +│ │ +│ Telegram references (minimal, stored as refs only) │ +│ ───────────────────────────────────────────────── │ +│ TelegramUserRef { remote_id, platform_alias } │ +│ TelegramChatRef { remote_id, chat_type } │ +│ TelegramMessageRef { remote_id, chat_ref } │ +└──────────────────────────────────────────────────────┘ +``` + +**Constraint:** + +``` +STATE ENGINE + │ + ├── MUST NOT mirror Telegram member databases + ├── MUST NOT store full Telegram user profiles + └── MUST store only what the SDK logic requires +``` + +--- + +## 2.9 TELEGRAM ADAPTER (EGRESS) + +``` +┌──────────────────────────────┐ +│ Action[] │ +│ (dispatched by Action Engine│ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ TELEGRAM ADAPTER │ +│ │ +│ Egress │ +│ ───────────────────────── │ +│ Translate Action → API call │ +│ Apply pacing │ +│ Handle rate limits │ +│ Retry on failure │ +│ Return result │ +└──────────────┬───────────────┘ + │ + ▼ +╔══════════════════════════════╗ +║ TELEGRAM PLATFORM ║ +╚══════════════════════════════╝ +``` + +**Transport implementation is hidden from the SDK:** + +``` +ACTION ENGINE + │ + ▼ +TELEGRAM ADAPTER INTERFACE + │ + ▼ +[ Bot API implementation hidden ] +[ Webhooks vs polling hidden ] +[ Retry strategy hidden ] +``` + +--- + +--- + +# PART 3 — PLUGIN SPECIFICATION + +--- + +## 3.1 PLUGIN INTERFACE CONTRACT + +Every plugin is a Logic Module. The interface is the contract. + +```typescript +interface LogicModule { + id : string // unique plugin identifier + version : string // semver + + match( + event : InternalEvent, + context : ResolvedContext + ): boolean + + execute( + event : InternalEvent, + context : ResolvedContext + ): Workflow | Action[] | null + + actions(): ActionType[] +} +``` + +Plugin registration: + +``` +SDK Core + │ + ▼ +Plugin Registry + │ + ├── register(module: LogicModule) + ├── resolve(event, context) → LogicModule | null + └── list() → LogicModule[] +``` + +--- + +## 3.2 PLUGIN DIRECTORY + +``` +plugins/ +│ +├── content/ Content creation, versioning, approval, publication +├── community/ Join requests, member events, moderation rules +├── support/ Support tickets, assignment, resolution lifecycle +├── scheduler/ Scheduled task creation and execution triggering +├── automation/ Rule evaluation engine, trigger-action pairs +├── broadcast/ Audience targeting, delivery job management +├── analytics/ Telemetry collection (non-blocking) +└── audit/ Immutable audit record generation +``` + +--- + +## 3.3 PLUGIN: CONTENT + +``` +match: + event_type IN [COMMAND, CALLBACK_QUERY] + AND command IN content commands + AND context.permissions ALLOWS content + +execute: + ├── CREATE → Workflow(content_create_workflow) + ├── EDIT → Workflow(content_edit_workflow) + ├── PREVIEW → Action[SendMessage(preview)] + ├── APPROVE → Workflow(approval_workflow) + └── PUBLISH → Workflow(publish_workflow) + +actions: + [SendMessage, EditMessage, DeleteMessage, PublishPost, PinMessage] +``` + +**Content workflow:** + +``` +content_create_workflow + │ + ├── Step 1: Collect content input + │ actions: [SendMessage(prompt)] + │ next: Step 2 + │ + ├── Step 2: Edit / Preview + │ WAIT for user input + │ actions: [SendMessage(preview)] + │ next: Step 3 + │ + ├── Step 3: Approval gate (if required) + │ Action[PublishPost].requires_approval = true + │ next: Step 4 + │ + └── Step 4: Publish + actions: [PublishPost, SendMessage(confirmation)] + next: null (terminal) +``` + +**Domain state:** + +```typescript +interface Content { + id : UUID + versions : ContentVersion[] + state : 'draft' | 'pending' | 'approved' | 'published' | 'archived' + chat_ref : TelegramChatRef + created_at : ISO8601 + updated_at : ISO8601 +} + +interface ContentVersion { + version_id : UUID + payload : string + state : 'draft' | 'preview' | 'approved' + created_at : ISO8601 +} +``` + +--- + +## 3.4 PLUGIN: COMMUNITY + +``` +match: + event_type IN [JOIN_REQUEST, MEMBER_EVENT] + +execute: + ├── JOIN_REQUEST → Workflow(join_request_workflow) + └── MEMBER_EVENT → Rule evaluation → Action[] | null + +actions: + [ApproveJoinRequest, DeclineJoinRequest, BanMember, + UnbanMember, MuteMember, SendMessage] +``` + +**Join request workflow:** + +``` +join_request_workflow + │ + ├── Step 1: Evaluate auto-approval rules + │ → if match: Action[ApproveJoinRequest] + │ → if no match: next Step 2 + │ + ├── Step 2: Notify owner + │ actions: [SendMessage(notification to owner)] + │ WAIT for owner decision + │ + └── Step 3: Execute decision + ├── APPROVE → Action[ApproveJoinRequest] + └── REJECT → Action[DeclineJoinRequest] +``` + +**Domain state:** + +```typescript +interface CommunityRule { + id : UUID + trigger : RuleTrigger + conditions : Condition[] + action : ActionType +} +``` + +--- + +## 3.5 PLUGIN: SUPPORT + +``` +match: + event_type == MESSAGE + AND chat.type == PRIVATE + AND NOT active_workflow + +execute: + → Workflow(support_ticket_workflow) + +actions: + [SendMessage, EditMessage] +``` + +**Support ticket workflow:** + +``` +support_ticket_workflow + │ + ├── Step 1: Create ticket + │ state: Ticket { state: 'open' } + │ actions: [SendMessage(acknowledgement to user)] + │ next: Step 2 + │ + ├── Step 2: Notify owner / assign + │ state: Ticket { state: 'assigned' } + │ actions: [SendMessage(notification to owner)] + │ WAIT for owner reply + │ + ├── Step 3: Owner replies + │ actions: [SendMessage(reply to user)] + │ next: Step 4 or loop Step 2 + │ + └── Step 4: Resolve + state: Ticket { state: 'resolved' } + actions: [SendMessage(resolution to user)] + next: null (terminal) +``` + +**Domain state:** + +```typescript +interface Ticket { + id : UUID + user_ref : TelegramUserRef + chat_ref : TelegramChatRef + state : 'open' | 'assigned' | 'in_progress' | 'resolved' | 'closed' + messages : TicketMessage[] + created_at : ISO8601 + resolved_at : ISO8601 | null +} +``` + +--- + +## 3.6 PLUGIN: SCHEDULER + +``` +match: + event_type == SCHEDULED + +execute: + → Resolve target Logic Module from task definition + → Delegate execution to target module + +actions: + [] (Scheduler does not produce actions directly) +``` + +**Scheduler model:** + +```typescript +interface ScheduledTask { + id : UUID + target_module : string // Logic Module ID + trigger_time : ISO8601 + payload : TaskPayload + state : 'pending' | 'triggered' | 'completed' | 'failed' +} +``` + +**Execution path:** + +``` +ScheduledTask.trigger_time reached + │ + ▼ +Synthetic SCHEDULED event created + │ + ▼ +Event Engine → Context Engine → Logic Router + │ + ▼ +Scheduler Plugin matches + │ + ▼ +Resolves target Logic Module + │ + ▼ +Target module executes + │ + ▼ +Actions → Action Engine → Adapter +``` + +**Constraint:** + +``` +SCHEDULER PLUGIN + │ + └── MUST NOT ──► Telegram Adapter directly + └── MUST ──► Synthetic event → Logic Router +``` + +--- + +## 3.7 PLUGIN: AUTOMATION + +``` +match: + any event_type + (evaluated after all other modules if no match found) + +execute: + ├── Evaluate rules against event + context + ├── If rule matches → resolve target action or module + └── If no rule matches → null + +actions: + [SendMessage, DeleteMessage, BanMember, MuteMember] +``` + +**Rule evaluation:** + +``` +InternalEvent + ResolvedContext + │ + ▼ +RULE ENGINE + │ + ├── Rule 1: evaluate conditions → false → skip + ├── Rule 2: evaluate conditions → false → skip + └── Rule 3: evaluate conditions → true + │ + ▼ + ACTION or MODULE DISPATCH +``` + +**Domain state:** + +```typescript +interface AutomationRule { + id : UUID + name : string + trigger : EventType + conditions : Condition[] + action : ActionType | ModuleID + is_active : boolean +} +``` + +--- + +## 3.8 PLUGIN: BROADCAST + +``` +match: + event_type == COMMAND + AND command IN broadcast commands + AND context.permissions ALLOWS broadcast + +execute: + → Workflow(broadcast_workflow) + +actions: + [SendMessage, ForwardMessage, SendPoll, SendFile] +``` + +**Broadcast workflow:** + +``` +broadcast_workflow + │ + ├── Step 1: Define payload + │ WAIT for content input + │ next: Step 2 + │ + ├── Step 2: Select audience + │ WAIT for audience selection + │ next: Step 3 + │ + ├── Step 3: Approval gate + │ Action[SendMessage * N].requires_approval = true + │ next: Step 4 + │ + └── Step 4: Execute delivery + actions: [SendMessage × audience] + next: null (terminal) +``` + +**Separation of concerns:** + +``` +Broadcast Plugin + │ + ├── Targeting logic (plugin responsibility) + ├── Payload construction (plugin responsibility) + └── Produces Action[] (then delegates) + │ + ▼ +Action Engine + │ + ├── Pacing (Action Engine / Adapter responsibility) + ├── Rate limiting (Adapter responsibility) + └── Retry (Adapter responsibility) +``` + +**Domain state:** + +```typescript +interface Broadcast { + id : UUID + payload : BroadcastPayload + audience : TelegramChatRef[] + state : 'draft' | 'pending' | 'approved' | 'delivering' | 'delivered' + created_at : ISO8601 + delivered_at : ISO8601 | null +} +``` + +--- + +## 3.9 PLUGIN: ANALYTICS + +``` +match: + never (analytics is not a primary logic handler) + +execute: + invoked as a side-effect hook, not by the Logic Router directly + +actions: + [] (analytics produces no Telegram actions) +``` + +**Non-blocking telemetry model:** + +``` +Logic Module + │ + ▼ +Core Workflow Completes + │ + ├────────────────────────────► BUSINESS RESULT + │ + └── emit TelemetryEvent (non-blocking, fire-and-forget) + │ + ▼ + Analytics Plugin + │ + ▼ + Metric aggregation + +ANALYTICS FAILURE + │ + └── MUST NOT affect core workflow result +``` + +**Tracked events:** + +``` +TelemetryEvent +├── content.published +├── ticket.created +├── ticket.resolved +├── broadcast.delivered +├── join_request.approved +├── join_request.declined +├── workflow.completed +├── workflow.failed +└── action.dispatched +``` + +--- + +## 3.10 PLUGIN: AUDIT + +``` +match: + never (audit is not a primary logic handler) + +execute: + invoked on every state mutation, not by Logic Router + +actions: + [] (audit produces no Telegram actions) +``` + +**Immutable audit model:** + +``` +State Mutation (any domain entity) + │ + ▼ +AUDIT PLUGIN (hook) + │ + ▼ +┌──────────────────────────────────────────────────────┐ +│ AuditRecord │ +├──────────────────────────────────────────────────────┤ +│ id : UUID │ +│ actor : TelegramUserRef | 'system' │ +│ action : string │ +│ target : DomainEntity reference │ +│ timestamp : ISO8601 │ +│ result : 'success' | 'failure' │ +│ payload : AuditPayload │ +└──────────────────────────────────────────────────────┘ + │ + ▼ +IMMUTABLE (no update, no delete) +``` + +--- + +--- + +# PART 4 — DATA MODEL + +--- + +## 4.1 TRANSPORT REFERENCES (Telegram-specific, minimal) + +```typescript +interface TelegramUserRef { + remote_id : number // Telegram user ID + platform_alias : string | null // username if available +} + +interface TelegramChatRef { + remote_id : number + chat_type : 'private' | 'group' | 'supergroup' | 'channel' +} + +interface TelegramMessageRef { + remote_id : number + chat_ref : TelegramChatRef +} +``` + +These are references, not profiles. The SDK does not mirror Telegram's user database. + +--- + +## 4.2 DOMAIN ENTITIES (Business state, SDK-owned) + +```typescript +interface Ticket { + id, user_ref, chat_ref, state, messages, created_at, resolved_at +} + +interface Content { + id, versions, state, chat_ref, created_at, updated_at +} + +interface ContentVersion { + version_id, payload, state, created_at +} + +interface Broadcast { + id, payload, audience, state, created_at, delivered_at +} + +interface Reminder { + id, user_ref, chat_ref, message, trigger_time, state +} + +interface Poll { + id, question, options, chat_ref, state, results +} + +interface Workflow { + id, module_id, current_step, state, context_snapshot, created_at, updated_at +} + +interface Session { + id, user_ref, chat_ref, data, expires_at +} + +interface AuditRecord { + id, actor, action, target, timestamp, result, payload +} + +interface ScheduledTask { + id, target_module, trigger_time, payload, state +} + +interface AutomationRule { + id, name, trigger, conditions, action, is_active +} + +interface CommunityRule { + id, trigger, conditions, action +} +``` + +--- + +## 4.3 CONTEXT OBJECTS (Runtime-only, not persisted) + +```typescript +interface ResolvedContext { + bot : BotConfig + chat : ChatContext + user : UserContext + permissions : PermissionSet + session : Session | null + locale : Locale + workflow : ActiveWorkflow | null +} + +interface BotConfig { + bot_id : string + features : string[] // enabled plugin IDs + locale : Locale +} + +interface UserContext { + ref : TelegramUserRef + access_level : 'owner' | 'admin' | 'member' | 'guest' +} + +interface PermissionSet { + allowed_actions : ActionType[] + allowed_modules : string[] +} + +interface ActiveWorkflow { + workflow_id : UUID + module_id : string + current_step : string + state : WorkflowState +} +``` + +--- + +--- + +# PART 5 — COMPLETE END-TO-END FLOWS + +--- + +## 5.1 STANDARD EVENT FLOW + +``` +╔══════════════════════════════╗ +║ TELEGRAM PLATFORM ║ +╚══════════════╦═══════════════╝ + │ Raw Update + ▼ +┌──────────────────────────────┐ +│ TELEGRAM ADAPTER (ingress) │ +└──────────────┬───────────────┘ + │ Raw payload + ▼ +┌──────────────────────────────┐ +│ EVENT ENGINE │ +│ → InternalEvent │ +└──────────────┬───────────────┘ + │ InternalEvent + ▼ +┌──────────────────────────────┐ +│ CONTEXT ENGINE │ +│ → ResolvedContext │ +└──────────────┬───────────────┘ + │ InternalEvent + ResolvedContext + ▼ +┌──────────────────────────────┐ +│ LOGIC ROUTER │ +│ → module.match() → dispatch │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ LOGIC MODULE │ +│ → Action[] or Workflow │ +└──────────────┬───────────────┘ + │ + ┌───────┴───────┐ + │ │ + ▼ ▼ + Action[] Workflow + │ │ + │ ▼ + │ ┌──────────────────────┐ + │ │ WORKFLOW ENGINE │ + │ │ → Step execution │ + │ │ → Action[] │ + │ └──────────┬───────────┘ + │ │ + └───────┬───────┘ + │ Action[] + ▼ +┌──────────────────────────────┐ +│ ACTION ENGINE │ +│ → validate │ +│ → approval gate (if needed) │ +│ → dispatch │ +└──────────────┬───────────────┘ + │ validated Action[] + ▼ +┌──────────────────────────────┐ +│ STATE ENGINE │ +│ → persist domain state │ +└──────────────┬───────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ TELEGRAM ADAPTER (egress) │ +│ → translate Action → API │ +│ → pacing, rate limit, retry │ +└──────────────┬───────────────┘ + │ + ▼ +╔══════════════════════════════╗ +║ TELEGRAM PLATFORM ║ +╚══════════════════════════════╝ + │ + ┌───────┴───────┐ + │ │ + ▼ ▼ + TelemetryEvent AuditRecord + │ │ + ▼ ▼ + Analytics Immutable log + (non-blocking) +``` + +--- + +## 5.2 WORKFLOW RESUME FLOW + +``` +Telegram Update arrives + │ + ▼ +Event Engine → InternalEvent + │ + ▼ +Context Engine + │ + ▼ +context.workflow != null ? + │ + ├── YES + │ │ + │ ▼ + │ Logic Router + │ │ + │ └── Skip module matching + │ │ + │ ▼ + │ Workflow Engine + │ │ + │ └── Resume at current_step + │ + └── NO + │ + ▼ + Logic Router (normal matching) +``` + +--- + +## 5.3 APPROVAL GATE FLOW + +``` +Action Engine receives Action + │ + ▼ +Action.requires_approval == true ? + │ + ├── NO ──► dispatch to adapter immediately + │ + └── YES + │ + ▼ + APPROVAL WORKFLOW + │ + ├── Notify owner + │ Action[SendMessage(approval request)] + │ + ├── WAIT for owner decision + │ + ├── APPROVED + │ │ + │ └── dispatch original Action to adapter + │ + └── REJECTED + │ + └── cancel Action + │ + └── emit AuditRecord +``` + +--- + +## 5.4 ANALYTICS AND AUDIT SIDE-EFFECTS + +``` +Core Workflow + │ + ├── completes successfully + │ │ + │ ├──── emit TelemetryEvent (non-blocking) + │ │ │ + │ │ └── Analytics Plugin + │ │ │ + │ │ └── MUST NOT block or reverse core flow + │ │ + │ └──── State Engine persists mutation + │ │ + │ └── Audit Plugin (hook) + │ │ + │ └── AuditRecord (IMMUTABLE) + │ + └── core result delivered to Telegram +``` + +--- + +--- + +# PART 6 — ARCHITECTURAL RULES + +--- + +## 6.1 HARD BOUNDARIES + +``` +Rule 1: Telegram Adapter is the only component that communicates with Telegram. + +Rule 2: Raw Telegram updates MUST NOT enter the Logic Router or any Logic Module. + +Rule 3: Logic Modules MUST NOT call the Telegram Adapter directly. + +Rule 4: Logic Modules MUST NOT call each other directly. + +Rule 5: The Context Engine MUST run before any Logic Module executes. + +Rule 6: The Workflow Engine MUST resume an active workflow + before the Logic Router evaluates module matches. + +Rule 7: Modules declare their permitted action types via actions(). + The Action Engine enforces this at dispatch time. + +Rule 8: Analytics telemetry MUST be non-blocking. + Analytics failure MUST NOT reverse a completed workflow. + +Rule 9: Audit records are IMMUTABLE once written. + +Rule 10: The State Engine MUST NOT store full Telegram user profiles. + Only TelegramUserRef, TelegramChatRef, TelegramMessageRef are permitted. +``` + +--- + +## 6.2 EXTENSIBILITY RULES + +``` +Rule E1: Any new feature MUST be implemented as a Logic Module plugin. + No new features are added to SDK Core. + +Rule E2: A Logic Module plugin MUST implement the full LogicModule interface. + +Rule E3: The Telegram Adapter MAY be replaced with any adapter + that conforms to the Ingress / Egress interface contracts. + SDK Core and all Logic Modules are unaffected by adapter replacement. + +Rule E4: The SDK is messaging-platform-independent at every layer + except the Telegram Adapter. + Supporting a second messaging platform requires only a new Adapter. +``` + +--- + +## 6.3 THE UNIVERSAL EXECUTION PRINCIPLE + +``` +Event + ↓ +Context + ↓ +Decision (Logic Module or Workflow Step) + ↓ +Action[] + ↓ +Adapter + ↓ +Result + ↓ +State + Telemetry + Audit +``` + +This is the invariant execution sequence for every SDK operation. + +--- + +*End of Document — SDK-SPEC-001* From a8332512c02749ff629b5f3dff78e05a86582221 Mon Sep 17 00:00:00 2001 From: Marc Deniel Date: Sat, 12 Sep 2026 16:57:58 +0800 Subject: [PATCH 02/10] Add Docker Image CI workflow --- .github/workflows/docker-image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..3f53646 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) From c66daabae9aa926164126efa76791efe9d7cb68b Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sat, 12 Sep 2026 17:59:16 +0800 Subject: [PATCH 03/10] Delete .github/workflows/docker-image.yml Delete --- .github/workflows/docker-image.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 3f53646..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) From 7abd44c32cb979aebc0a50453f9e53dadcc06abf Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sat, 12 Sep 2026 18:02:42 +0800 Subject: [PATCH 04/10] Add GitHub Actions workflow for Docker image CI --- .github/workflows/docker.image.yml | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/docker.image.yml diff --git a/.github/workflows/docker.image.yml b/.github/workflows/docker.image.yml new file mode 100644 index 0000000..f65cadb --- /dev/null +++ b/.github/workflows/docker.image.yml @@ -0,0 +1,31 @@ +name: ci + +on: + push: + branches: + - "main" + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKER_USER }} + password: ${{ secrets.DOCKER_PAT }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: cloud + endpoint: "angelancajas98/cmake" + - name: Build and push + uses: docker/build-push-action@v6 + with: + tags: "${{ vars.DOCKER_USER }}/gramiojs/telegram-bot-api:latest" + # For pull requests, export results to the build cache. + # Otherwise, push to a registry. + outputs: ${{ github.event_name == 'pull_request' && 'type=cacheonly' || 'type=registry' }} + From e0157c13561cf1e42c2daad4977ce0694b1acecc Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sat, 12 Sep 2026 19:44:47 +0800 Subject: [PATCH 05/10] Delete .github/workflows/docker.image.yml --- .github/workflows/docker.image.yml | 31 ------------------------------ 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/docker.image.yml diff --git a/.github/workflows/docker.image.yml b/.github/workflows/docker.image.yml deleted file mode 100644 index f65cadb..0000000 --- a/.github/workflows/docker.image.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: ci - -on: - push: - branches: - - "main" - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKER_USER }} - password: ${{ secrets.DOCKER_PAT }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver: cloud - endpoint: "angelancajas98/cmake" - - name: Build and push - uses: docker/build-push-action@v6 - with: - tags: "${{ vars.DOCKER_USER }}/gramiojs/telegram-bot-api:latest" - # For pull requests, export results to the build cache. - # Otherwise, push to a registry. - outputs: ${{ github.event_name == 'pull_request' && 'type=cacheonly' || 'type=registry' }} - From 50abc22c0a4f12dbff934bfb761b9a477b71302e Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sun, 13 Sep 2026 01:05:00 +0800 Subject: [PATCH 06/10] Create SECURITY.md for security policy Added a security policy document outlining supported versions and vulnerability reporting. --- SECURITY.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..034e848 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. From f1b1ed02da9c3637b07f04e7b81fa55e8524a9c5 Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sun, 13 Sep 2026 01:47:38 +0800 Subject: [PATCH 07/10] Add GitHub Actions workflow for GitHub Pages deployment This workflow automates the deployment of static content to GitHub Pages on push to the main branch or manually via the Actions tab. --- .github/workflows/static.yml | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/static.yml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..460f782 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 From acb7c17ce68cd970c73ba6d28f672e53308f10ad Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:27:05 +0800 Subject: [PATCH 08/10] Refactor Docker publish workflow configuration --- .github/workflows/docker-publish.yml | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..3fb1b3c --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,97 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '21 8 * * *' + push: + branches: [ "main" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "main" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + job: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 5069a453c3c5643d97875d21f2d11da0e8954e3b Mon Sep 17 00:00:00 2001 From: angelancajas98-droid <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:30:34 +0800 Subject: [PATCH 09/10] Potential fix for code scanning alert no. 1: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34d311f..51d4251 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,9 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + jobs: entrypoint: runs-on: ubuntu-latest From 82322c813f2664651db20406fbdf6fc9ac3e040e Mon Sep 17 00:00:00 2001 From: TAKE THE RISK <299915788+angelancajas98-droid@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:53:41 +0800 Subject: [PATCH 10/10] Add GitHub Actions workflow for GKE deployment This workflow builds a Docker container, publishes it to Google Container Registry, and deploys it to GKE on push to the main branch. It includes steps for authentication, Docker login, building the image, and deploying using Kustomize. --- .github/workflows/google.yml | 116 +++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 .github/workflows/google.yml diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000..0b5c7d1 --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,116 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide