chatGPT-discord-bot

Architecture and operational decisions

The original Python/discord.py entry point and module responsibilities remain. Shared mutable client history, implicit provider selection and the polled global queue were replaced with explicit dependencies and scoped state.

flowchart LR
    D[Discord commands and events] --> C[ConversationService]
    D --> M[MediaService]
    D --> I[ImageSearchService]
    I --> X[xAI ImageSearchProvider]
    D --> O[Owner CLIAuth]
    O --> L[Isolated CLIProvider / DockerRunner]
    C --> S[(SQLite Store)]
    M --> S
    C --> A[Official or compatible APIProvider]
    C --> L
    M --> P[Official MediaProvider]

src/aclient.py constructs resources in setup_hook and closes them on shutdown. src/bot.py supplies slash-command transport and captures the audience before any request. API clients are not created at import time. Reconnection does not create extra workers. src/config.py validates administrator TOML into backend/model objects; Discord supplies aliases only.

src/service.py owns admission, locking, context budgets and conversation changes. The same gate covers media, image search and text: a request acquires its conversation lock before a concurrency slot, so queued turns in one conversation do not occupy slots needed by unrelated conversations. Locks are removed when no requests use them. Admission and deadlines bound queued work. Attachment input downloads occur inside media admission, before any paid submission.

Every chat includes the selected backend kind and model ID in its system context. Retained replies remain intact and may come from previous models; their identity claims are not treated as current routing metadata. /provider without an alias reads the scoped selection under the same conversation lock and makes no provider request. Endpoint URLs, credentials and account identifiers are not included in the identity context or status response.

src/storage.py stores successful user/assistant pairs atomically as one row update. Keys hash bot ID, guild/DM namespace, channel/thread ID, user ID and audience. Persona/model changes preserve retained text but invalidate native sessions. An exclusive process lock prevents separate bot processes from racing the same DB. Unknown schemas are rejected before table creation. Retention limits both turns and inactivity; failed text generations do not enter the history.

src/providers.py translates text contracts and owns bounded pooled HTTP clients. src/domain.py defines capabilities and small text/session results. Parameters are validated against the implemented protocol subset instead of silently being dropped. No automatic provider fallback exists. Native CLI details and isolated runtime checks live only in src/cli.py; SQLite remains independent of those native transcripts. The request holds only the matching native UUID and context fingerprint, and persists session uncertainty before executing externally. The default CLI runtime is a dedicated rootless daemon. Explicit personal-bot Docker Desktop mode pins a seccomp profile by hash and verifies the effective in-container controls before invoking the native CLI. Both modes use the same tool restrictions, scoped state, argument construction and cleanup paths.

src/cli_accounts.py provides local native login administration and an owner-bound profile containing non-secret revision/status metadata. Account mode requires a single-user bot. An account lease spans context fingerprinting, native execution and history commit, preventing a concurrent re-login from attaching an answer to the wrong account revision. It also serializes native credential refresh across model aliases and conversations. runtime/account-runtime.mjs transfers only allowlisted native credential files between two bot-owned volumes inside Docker; transcripts remain scoped to their conversation. Native CLI binaries perform all authentication and refresh; the Python service never reads OAuth tokens.

src/cli_auth.py bounds owner-only account operations by backend, independent of conversation aliases. Discord always uses a private response. Codex/Grok device login observes the native subprocess output through a bounded, provider-specific prompt reader and relays only a validated official URL and user code. No custom OAuth client is introduced. Claude’s native login stays in the local terminal. Cancellation and shutdown wait for process/container cleanup; authentication revision changes continue to preserve bot-owned conversation history.

src/art.py owns media capabilities, submission records, bounded polling and artifact retrieval. The job states are submitting, unknown, pending, failed, ready, and delivered. A crash during submission becomes unknown; saved video operations can be retrieved without another POST. Polling cancellation keeps the operation. A provider terminal failure becomes failed. Image bytes are ephemeral, so a ready image whose Discord delivery fails is not automatically regenerated.

src/search.py sends an explicit current query to xAI image search, without conversation history. It requires a completed search tool and validates/deduplicates at most three public HTTPS image URLs. The host does not download these URLs; Discord renders them through its image proxy. Search shares bounded admission and cancellation with text/media, while the billing mode and model alias stay explicit.

utils/message_utils.py keeps the original Discord transport/audience and disables mentions. Text longer than 1900 characters becomes one UTF-8 attachment, preserving code and keeping send counts bounded. Media uses binary attachments with both administrator and Discord size limits. Buffers close on success and failure.

The design does not provide distributed workers, shared group histories, general agent tools, streaming chat or automatic provider discovery. Those features need separate ownership, safety and billing designs. API availability, deployment restrictions and supported protocol subsets are in provider support and CLI setup.