Skip to content

About

The long-awaited solution to AJ

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

CAC Admin Bot

Cosmos Airdrops Chat administration bot built with Telegraf in TypeScript. Combines advanced moderation features with a unified wallet system for JUNO cryptocurrency, using an internal ledger architecture backed by SQLite.

Features

Role Management

  • Four-tier role hierarchy: owner > admin > elevated > pleb
  • Owner: Full control including wallet/treasury access, role promotions, bot configuration
  • Admin: Moderation powers (jail, restrictions) but NO access to funds, treasury, or config
  • Elevated: Basic user with wallet access, can view lists and statistics
  • Pleb: Default role for all users

Unified Wallet System

  • Single JUNO wallet with internal ledger for all users
  • Automatic deposit detection via RPC monitoring with memo-based routing
  • Instant, fee-free internal transfers between users
  • Secure withdrawal flow with transaction locking
  • Complete audit trail of all financial operations
  • See LEDGER.md for technical details
  • See ADMIN_MANUAL.md for operational documentation

Open Giveaway System

  • Any user can create giveaways funded from their own balance
  • Owners/admins can fund giveaways from treasury
  • Configurable slot counts (10, 25, 50, 100 users)
  • Users claim slots via inline button
  • Per-giveaway escrow accounts for fund isolation
  • Creators can cancel and reclaim unclaimed funds

Content Moderation

  • User Restrictions: Block specific users from stickers, URLs, media, regex patterns, or chance-based random deletes
  • Global Restrictions: Apply content rules to all non-elevated users
  • Jail System: Temporary mutes with configurable duration and bail payments
  • Safe Regex: Timeout-protected pattern matching prevents ReDoS attacks
  • See REGEX_PATTERNS.md for pattern examples

Violation Tracking

  • Log user violations with configurable penalties
  • Fine system with USD-to-JUNO conversion
  • Bail payments for early jail release

Installation

git clone <repo-url> && cd cacmin-bot
bun install
cp .env.example .env
# Edit .env with required configuration
bun run setup-db
bun run build
bun run dev           # Development

Required Environment Variables:

  • BOT_TOKEN: Telegram Bot API token
  • OWNER_ID: Primary owner Telegram user ID
  • BOT_TREASURY_ADDRESS: Juno wallet address
  • BOT_TREASURY_MNEMONIC: Wallet seed phrase (24 words)
  • JUNO_RPC_URL: Primary Juno RPC endpoint

Telegram uses https://api.telegram.org by default. A deployment using the local Telegram Bot API and its media gateway can set:

TELEGRAM_API_ROOT=http://tgbotapi.lxd:8081
TELEGRAM_FILE_ROOT=http://tgbotapi.lxd:8082

Both values must be bare HTTP(S) origins. TELEGRAM_FILE_ROOT is optional with the public API, but is required if getFile returns an absolute local path.

Optional MEMBER_TAGS assigns a custom member tag whenever a listed user joins a group. Value is comma-separated <user_id>:<tag> pairs, keyed by the numeric Telegram user id (usernames are not used, they are mutable). The bot must be a group admin with the "Manage Tags" permission (can_manage_tags); tag values are 0-16 characters with no emoji. Example:

MEMBER_TAGS=123456789:Neil,987654321:VIP

Rebuild Options: ./rebuild.sh [--dev|--quick|--full]

Production Deployment

GitHub Release (Recommended)

wget https://github.com/cac-group/cacmin-bot/releases/latest/download/cacmin-bot-dist.tar.gz
tar -xzf cacmin-bot-dist.tar.gz
sudo ./install.sh

The production installer is intentionally preparation-first. It builds or validates the release, installs root-owned code at /opt/cacmin-bot, installs both update units, disables/stops the bot and update timer, stops any running updater invocation, and verifies all three CACMin units are inactive. It does not touch unrelated services. Production runs with the managed Bun binary at /opt/bun/bin/bun; install it before running install.sh.

Configuration lives outside the code tree at /etc/cacmin-bot/cacmin-bot.env (root:cacmin-bot, mode 0640). Runtime data and file logs remain service-owned under /opt/cacmin-bot/data and /opt/cacmin-bot/logs. At the approved cutover, activate the prepared service and updater explicitly:

sudo ./install.sh --activate

For the tgbotapi.lxd deployment, add the local API and media gateway origins to /etc/cacmin-bot/cacmin-bot.env:

TELEGRAM_API_ROOT=http://tgbotapi.lxd:8081
TELEGRAM_FILE_ROOT=http://tgbotapi.lxd:8082

The API root handles Bot API calls. The file root handles media whose getFile response contains an absolute server path; CACMin sends that path only as an encoded gateway query parameter. Do not expose the media gateway outside the trusted service network.

Preparation warns if an existing environment file has insecure metadata but never prints its contents. Activation refuses a missing environment file and normalizes then verifies its ownership and mode before enabling either unit.

The installer refuses its production paths on hosts other than tgbot. The update timer executes the root-owned /usr/local/libexec/cacmin-bot-auto-update, never a script writable by the bot service. Timer runs follow the rolling latest release. Tag-triggered GitHub deployments pass that exact tag with --release TAG --force, so production installs the artifact published by the triggering workflow rather than a newer or older latest artifact. A read-only release check does not call systemd or change the install:

sudo /usr/local/libexec/cacmin-bot-auto-update --check

See DEPLOYMENT.md for complete deployment documentation.

Manual Build

bun install && bun run build
sudo ./install.sh

Chat Explorer Indexer

When INDEXER_ENABLED=true, cacmin-bot is the single live writer for the shared telegram-chat-explorer dataset. Point INDEXER_DB_PATH at the active explorer SQLite DB, set INDEXER_DATASET_ID to the matching dataset ID, and set GROUP_CHAT_ID to the Telegram supergroup ID. The museum/API service must use LIVE_INDEX_ENABLED=false; running two Telegram consumers or two SQLite writers during migration can lose updates or double-handle bot actions.

For the CAC Museum deployment, keep INDEXER_EMBEDDINGS_ENABLED=false and use INDEXER_EMBED_TRIGGER_FILE=/opt/telegram-chat-explorer/state/embed-missing.trigger. After every INDEXER_EMBED_TRIGGER_BATCH_SIZE eligible text/caption inserts, the bot touches that trigger so teleindexer-embed-missing.path can run the explorer embedding worker and keep pgvector in sync.

The indexer also maintains the explorer dataset counters used by Grafana metrics (dataset_meta and live_index_state). Duplicate Telegram message IDs return before these counters are advanced.

Live author identity, ID-backed mentions, edits, and message_reaction old/new state are written into the explorer's immutable-user and interaction queue tables. Each distinct active reaction from the same user counts separately; repeated Telegram delivery is idempotent and reaction removal deletes only the removed active reaction. The greatest Telegram update_id is persisted per message/reactor so delayed or replayed older transitions cannot restore stale active-reaction state.

Production Topology and Recovery

On tgbot, the cacmin-bot account owns only its mutable data/log directories and joins teleindexer-data to write the shared explorer database, downloaded media, and embedding trigger. Application code remains root:root and is read-only to the service. The unit's write allowlist is limited to those own and shared paths.

Install and updater replacements move the previous code into /opt/cacmin-bot/.rollback/ before replacing it. If an updated active service does not start, the updater restores the previous code and attempts to restart that version. Rollback is armed before the first code move; partial move or activation failure restores every displaced entry. A failed activation leaves the bot, updater, and timer inactive and leaves the bot and timer disabled, so a reboot cannot create a second writer. For manual recovery, stop cacmin-bot.service, restore one rollback directory's dist, node_modules, package.json, bun.lock, and version.txt, confirm they are root:root without group/other write bits, then start the service and verify the writer counters and a read-only wallet/ledger status command before reopening moderation traffic.

Database recovery is separate from code rollback. Never replace /opt/cacmin-bot/data/bot.db while the service is running. Restore the database and its WAL/SHM-consistent backup as one maintenance operation, then verify the ledger, moderation state, and explorer writer freshness before starting the bot.

Service Management

sudo systemctl status cacmin-bot
sudo systemctl restart cacmin-bot
sudo journalctl -u cacmin-bot -f

Commands

Use /help in a DM with the bot for the interactive, role-based reference. The menu is keyboard-navigated: categories split into subcategories, and each section renders with a back button to the parent menu. Content is filtered by role, so sensitive topics and functions (treasury operations, wallet tests, moderation tools, and the like) are only visible to the tiers that may use them. The list below is the full current slash-command surface.

Core Help

  • /help - Open the DM help menu
  • /menu - Open the DM button menu (Wallet, Shared Accounts, Moderation, Lists, Roles, Statistics, Help)
  • /wallethelp - Show detailed wallet and treasury help

Wallet and Deposits (All Users unless noted)

  • /balance or /bal - View your internal JUNO balance
  • /deposit - Get your deposit address and required memo
  • /verifydeposit <txhash> - Verify and credit a deposit by transaction hash
  • /withdraw <amount> <junoAddress> - Withdraw JUNO to an external wallet
  • /send <amount> <recipient> or /transfer <amount> <recipient> - Send to @username, user ID, or juno1... address
  • /transactions [@user|userId] or /history [@user|userId] - View your recent transactions; owners can target another user
  • /verifydeposit <txhash> - Verify and credit your own deposit by transaction hash
  • /claimdeposit <txhash> - If a deposit was missed, verify it on-chain and credit the user id in the memo
  • /checkdeposit <txhash> or /checktx <txhash> - Check whether a deposit was already processed
  • /unclaimeddeposits - View the current UNCLAIMED deposit pool and recent invalid-memo deposits
  • /fundtreasury <amount> - Move funds from your balance into the game treasury
  • /fundtreasury deposit - Get external deposit instructions for the game treasury
  • /treasurybalance or /gamebalance - View the game treasury balance (admin+)
  • /contributetreasury <amount> - Contribute to the treasury from your balance (admin+)
  • /withdrawtreasury <amount> - Withdraw treasury funds back to your balance (owner)
  • /treasurytransfer <@user|userId> <amount> - Send treasury funds to a user's balance (owner)
  • /treasurywithdraw <amount> <address> - Withdraw treasury funds to an external Juno address (owner)
  • /treasuryreserve <amount> <to|from> - Move funds between the treasury and reserve accounts (owner)
  • /treasurydetails - View full treasury details: address, balances, and recent transactions (owner)

Shared Accounts

  • /createshared <name> <display_name> [description] - Create a shared account; quote multi-word display names or descriptions (elevated+)
  • /listshared - List all shared accounts (elevated+)
  • /myshared - List the shared accounts you can access
  • /sharedbalance <account> - View a shared account balance
  • /sharedinfo <account> - View account metadata and member permissions
  • /sharedsend <account> <@username|user_id> <amount> [description] - Send from a shared account to another bot user
  • /shareddeposit <account> <amount> - Move funds from your balance into a shared account
  • /sharedhistory <account> [limit] - View shared-account transaction history
  • /grantaccess <account_name> <@username|user_id> <level> [spend_limit] - Grant shared-account access
  • /revokeaccess <account_name> <@username|user_id> - Remove shared-account access
  • /updateaccess <account_name> <@username|user_id> <level> [spend_limit] - Change a member's access level
  • /deleteshared <account> - Delete an empty shared account

User Status and Visibility

  • /mystatus - View your role, warnings, jail status and bail, activity stats, and unpaid fines
  • /jails - View currently jailed users
  • /violations - View your violation history
  • /viewwhitelist - View whitelisted users
  • /viewactions - View active global restrictions
  • /ratelimit [user] - View your 15-minute, 1-hour, and 24-hour character usage, including rollover capacity (admins may pass a user to view another user's usage)
  • /ratelimits - Explain rate-limit windows, rollover, enforcement, and paid resets
  • /ratelimitreset [user] <15m|1h|24h> - Purchase a JUNO reset for one usage window
  • /verifyratelimitreset <txhash> - Verify a purchased rate-limit reset
  • /listadmins - View all elevated, admin, and owner users (elevated+)
  • /jailstats [@user|userId] - View active jail info or a specific user's jail history (elevated+)

Message Rate Limits

Rate limits are opt-in per-user moderation controls. Configure a user's 15-minute base character budget with /setratelimit <user> <15m_chars> (admin+). The default derived budgets are 2x the base for one hour and 4x the hourly budget for 24 hours. Emoji count as 2 characters, stickers as 5 characters, and a shared image (photo or file) as 25 characters.

Each window can carry unused capacity into only its immediately following matching window. Current-period capacity is consumed first; rollover never compounds. A message that would exceed any active window is deleted, the user's current Telegram permissions are captured, and the user is muted until the relevant window resets. The scheduled cleanup restores the captured permissions without overriding an active jail.

Use /ratelimit for current usage and /ratelimits for a concise explanation. Users can purchase a reset with /ratelimitreset <15m|1h|24h> in a DM or group. A reset request may target another configured user, so anyone can pay the applicable JUNO fee. Reply to the payment instructions with the transaction hash, or send it in a DM. The bot verifies an exact successful transfer to the treasury and rejects transaction-hash reuse before clearing the selected window.

Stickers

  • /cac - Send the first sticker from the CACGifs pack
  • /sendsticker [name] - Send a sticker from the CACGifs pack by name (currently only first is configured; /sendsticker first matches /cac)
  • /getsticker - Reply to a sticker to get its file_id

Giveaways and Games

  • /giveaway <amount> - Create an open giveaway funded from your balance
  • /cancelgiveaway [id] - Cancel one of your active giveaways
  • /roll <amount> - Play the roll game
  • /rollstats - View your roll-game statistics
  • /rollodds - View roll-game rules and probabilities
  • /duel <@username|userId> <amount> - Challenge another user to a wagered duel
  • /duelstats - View your duel stats
  • /duelhistory [limit] - View your recent completed duels
  • /duelcancel - Cancel your pending outgoing duel

Bail and Verification

  • /paybail [<@username|userId>] - Show the Jailing ID, bail amount, and payment address; reply /paybail to a jailed user's message to pay for them
  • /verifybail <txhash> - Verify a bail payment (the tx memo must contain the Jailing ID) and release the user
  • /bailhelp - Get complete bail payment and verification details in a DM
  • /payfines, /payfine - Legacy aliases that show the same bail instructions
  • /verifypayment <txhash> - Legacy alias for /verifybail

Moderation and Restrictions

  • /jail <@username|userId> <minutes> ["reason"] or /silence <@username|userId> <minutes> ["reason"] - Jail a user for a fixed duration, optionally recording a quoted reason (admin+)
  • /unjail <@username|userId> or /unsilence <@username|userId> - Release a jailed user (admin+)
  • /warn <@username|userId> <reason> - Issue a warning and violation (admin+)
  • /addrestriction <@username|userId> <type> [action] [until] [severity] [threshold] [jailDuration] - Add a user restriction (admin+)
  • /listrestrictions <@username|userId> - View a user's restrictions (elevated+)
  • /removerestriction <@username|userId> [type] - Remove one restriction, or omit [type] to remove them all (elevated+)
  • /clearrestrictions <@username|userId> - Remove all restrictions from a user (elevated+)
  • /bangif [@username|userId] [gif_id] [-g] - Ban a GIF: reply to it, or name a user and id; add -g for a global ban (admin+)
  • /getgifid - Reply to a GIF to get its file_unique_id (admin+)
  • /regexhelp - Show regex restriction examples (admin+)
  • /addaction <restriction> [restrictedAction] - Add a global restriction (admin+)
  • /removeaction <restriction> - Remove a global restriction (admin+)
  • /setratelimit <user> <15m_chars> - Configure a user's base character limit; 1-hour is 2x and 24-hour is 4x the hourly limit. One period of unused capacity rolls over without compounding (admin+)
  • /listratelimits - List every user with an active rate limit and how much of each window remains (admin+)
  • /clearratelimit <user> - Remove a user's character rate limit (admin+)
  • /resetratelimit <user> - Clear a user's accumulated rate-limit usage and any active rate-limit mute without changing their configured limits (admin+)
  • /addwhitelist <@username|userId> and /removewhitelist <@username|userId> - Manage whitelist entries (admin+)

Deposit Recovery and Treasury Ops

  • /processdeposit <txhash> [userId|@username] - Recover or allocate any deposit: the memo user by default, or a target user for an unclaimed deposit (admin+)
  • /botbalance - View the bot's on-chain wallet balance (owner)
  • /treasury - View treasury and ledger status (owner)
  • /walletstats - View wallet and reconciliation stats (owner)
  • /reconcile - Force a reconciliation check (owner)
  • /adjustbalance <amount> <debit|credit> [reason] - Correct ledger discrepancies by adjusting the bot treasury balance (owner)

Roles and Ownership

  • /setowner - Register yourself as owner if your Telegram ID is already configured in OWNER_ID/OWNER_IDs
  • /grantowner <@username|userId> - Grant owner privileges to another user (owner)
  • /makeadmin <@username|userId> - Promote a user to admin (owner)
  • /elevate <@username|userId> - Promote a user to elevated (admin+)
  • /revoke <@username|userId> - Demote an elevated or admin user (admin+)

Violations and Moderation Ops

  • /payallfines <@username|userId> - Release a jailed user by covering their bail from the system reserve (admin+)
  • /junoprice - View the current JUNO price (owner)
  • /clearviolations <@username|userId> - Clear all violations for a user (owner)
  • /crawlidentities [count|reset] - Backfill missing usernames and profiles for known members (owner)
  • /stats - View overall bot statistics (owner)

Spam-Reaction Pattern Controls

  • /listspamreacts - View active custom spam-reaction patterns plus built-ins (admin+)
  • /spamreacthelp - Show the spam-reaction field and pattern guide (admin+)
  • /addspamreact [pattern] [bio|channel|both] - Add a custom spam-reaction pattern (owner)
  • /removespamreact <id> - Remove a custom spam-reaction pattern (owner)
  • /testspamreact <pattern> <sample> - Test a spam-reaction pattern without saving it (owner)

Identity Block Pattern Controls

  • /listidentityblocks - View active name and username block patterns plus built-ins (admin+)
  • /identityblockhelp - Show the identity-block field and pattern guide (admin+)
  • /addidentityblock <pattern> [name|username|both] - Add a custom identity block pattern (owner)
  • /removeidentityblock <id> - Remove a custom identity block pattern (owner)
  • /testidentityblock <pattern> <sample> - Test an identity block pattern without saving it (owner)

Architecture

src/
  bot.ts              # Entry point, handler registration, periodic tasks
  config.ts           # Environment configuration
  database.ts         # SQLite schema and query functions
  commands/           # Command handlers by feature
    giveaway.ts       # Giveaway creation and management
    wallet.ts         # Balance, deposit, withdraw, send
    moderation.ts     # Jail, restrictions, warnings
    roles.ts          # Role management
  handlers/           # Feature-specific handlers
    callbacks.ts      # Inline keyboard callback handlers
    restrictions.ts   # Content filter logic
  services/           # Business logic layer
    ledgerService.ts          # Internal balance operations
    unifiedWalletService.ts   # On-chain wallet operations
    jailService.ts            # Jail/bail management
    transactionLockService.ts # Concurrency control
  middleware/         # Request pipeline
    auth.ts           # User identification
    permissions.ts    # Role-based access control
    messageFilter.ts  # Content restriction enforcement
  utils/              # Shared utilities
    precision.ts      # Cryptocurrency math (6 decimals)
    safeRegex.ts      # Timeout-protected regex
    keyboards.ts      # Inline keyboard builders

Key Patterns

  • Service-oriented: Stateless services with static methods
  • Double-entry accounting: Every transaction creates balanced ledger entries
  • Transaction locks: Prevent double-spending during withdrawals
  • Escrow accounts: Per-giveaway fund isolation
  • Memo decoding: Canonical decodeTxRaw memo extraction for deposit allocation

See LEDGER.md for detailed token flow documentation.

Testing

bun run test                    # Run all tests
bun run test:watch              # Watch mode
bun run test:coverage           # Coverage report
bun run test tests/unit         # Unit tests only
bun run test tests/integration  # Integration tests only

Documentation

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Make granular commits with clear messages
  4. Open a pull request

License

MIT License. See LICENSE for details.

About

The long-awaited solution to AJ

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages