# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview A Telegram bot built with GramIO that enables role-based mentions in group chats. Users can create custom roles, join/leave them, and mention all role members at once using @mentions or commands. ## Development Commands - `pnpm dev` - Run bot in development mode with hot reload - `pnpm start` - Run bot in production mode - `pnpm drizzle-kit generate --config=src/shared/database/config.ts` - Generate database migrations after schema changes - `pnpm drizzle-kit migrate --config=src/shared/database/config.ts` - Apply pending migrations to database - `pnpm drizzle-kit studio --config=src/shared/database/config.ts` - Open Drizzle Studio for database inspection ## Architecture ### Core Components **Bot Entry Point** (`src/index.ts` → `src/bot/index.ts`) - Bot initialization uses GramIO framework - Commands are auto-loaded from `src/bot/commands/` using `@gramio/autoload` - Two middleware handlers in `src/bot/index.ts`: 1. Message handler for @mention syntax (e.g., `@rolename`) - triggers role mentions 2. Command handler for bare commands (e.g., `/rolename`) - also triggers role mentions - Bot username is captured on startup to properly handle commands with @botname suffix **Database** (PostgreSQL with Drizzle ORM) - Schema defined in `src/shared/database/schema.ts` - Three tables: `roles` (id, slug, aliases, chatId), `role_members` (id, roleId, userId), and `chat_configs` (id, chatId, roleManagePermission, roleMentionPermission) - Configuration in `src/shared/database/config.ts` - Path aliases use `@/*` mapping to `./src/*` **Services** (`src/shared/services/`) - `RoleService` - All role and role member database operations - `MentionService` - Handles mentioning all members of a role - Chunks members into groups of 5 to avoid hitting Telegram's limits - Builds mention entities using GramIO's `mention()` helper - Sends multiple messages if role has >5 members, with pagination indicators - `ChatConfigService` - Manages per-chat permission configuration - Lazy initialization via `getOrCreate()` - configs created on first access - Defaults: `all_admins` for role management, `everyone` for mentions - `PermissionService` - Centralized permission checking logic - `canManageRoles()` - Checks if user can add/delete roles based on chat config - `canMentionRoles()` - Checks if user can trigger role mentions based on chat config **Commands** (`src/bot/commands/`) - Admin commands require chat admin permissions (checked via `isChatAdmin()`) - `/roleadd ` - Create new role (permission configurable, updates bot commands dynamically) - `/roledel` - Delete role (permission configurable) - `/join ` - Join a role (always available to everyone) - `/leave ` - Leave a role (always available to everyone) - `/myroles` - List your roles - `/roles` - List all roles in chat - `/config` - View and configure permissions (any admin can view, owner can change via inline buttons) - `/reload` - Refresh admin cache (any admin) **Role Triggering** - Roles can be triggered two ways: 1. Via @mention in any message (e.g., "hey @developers") 2. Via bare command (e.g., "/developers" or "/developers@botname") - Both methods call `MentionService.mentionAll()` **Utilities** - `src/bot/utilities/perms.ts` - Permission checking utilities - `isChatAdmin()` - Checks if user is admin (uses 1-hour cache) - `isChatOwner()` - Checks if user is chat creator/owner - `getAdminInfo()` - Returns detailed admin info with granular permissions - `hasAdminPermission()` - Checks specific Telegram admin permissions (can_promote_members, can_change_info, can_manage_chat) - `refreshAdminCache()` - Clears admin cache for a specific chat - Admin cache: 1-hour TTL to reduce Telegram API calls, falls back to stale data on error - Handles hidden admins including anonymous admin bot ID 1087968824 - `src/bot/utilities/constants.ts` - Role name validation regex and reserved command names - `src/shared/utilities/chunk.ts` - Array chunking utility for member batching ### Important Patterns **Permission System** - Chat owners can configure permissions via `/config` command with inline keyboard navigation - Role management options: `everyone`, `all_admins`, `admin_can_promote_members`, `admin_can_change_info`, `admin_can_manage_chat`, `only_owner` - Role mention options: `everyone`, `all_admins`, `only_owner` - Permissions checked via `PermissionService` which consults `chat_configs` table - Join/leave functionality always available to everyone (no configuration) - Admin cache with 1-hour TTL reduces Telegram API calls - use `/reload` to manually refresh **Role Name Validation** - Must match `ROLE_NAME_REGEX`: 4-32 characters, letters only - Cannot use `RESERVED_NAMES` (built-in command names) **Chat Scope** - All roles are scoped to `chatId` - same role name can exist in different chats - Bot only works in groups/supergroups, not private chats **Dynamic Command Registration** - When admin creates/deletes roles, bot updates chat-specific command list via `setMyCommands()` with scope type 'chat' - This allows autocomplete for role names in that specific chat **Environment Variables** - Required: `DATABASE_URL`, `BOT_TOKEN` - Optional: `NODE_ENV` (defaults to 'production') - Loaded via Node's `loadEnvFile()` from `.env` file ### Database Migrations After modifying `src/shared/database/schema.ts`, generate and apply migrations using drizzle-kit with the explicit config path: - `pnpm drizzle-kit generate --config=src/shared/database/config.ts` - `pnpm drizzle-kit migrate --config=src/shared/database/config.ts` The migration output directory is `./drizzle/`. **Current Schema:** - `roles` - Role definitions with slug, aliases, and chatId - `role_members` - User memberships in roles - `chat_configs` - Per-chat permission configuration (lazy-initialized) - Enums: `role_manage_permission` and `role_mention_permission` - Default values ensure backwards compatibility with existing chats