How to Configure Project Threads Mode for Effective Telegram Topic Routing
Enable project threads mode by setting ENABLE_PROJECT_THREADS=true and defining PROJECT_THREADS_MODE as either private or group, then provide a PROJECTS_CONFIG_PATH YAML file that maps project slugs to directory paths.
The claude-code-telegram bot supports advanced conversation routing through project-specific Telegram forum topics. By configuring project threads mode, developers can isolate discussions per codebase directory, ensuring that messages about the API service land in a distinct topic from those about the web frontend. This guide walks through the complete configuration based on the RichardAtCT/claude-code-telegram source code.
Prerequisites and Environment Setup
Required Environment Variables
The thread-routing subsystem is controlled through Pydantic settings defined in src/config/settings.py (lines 200–228). To activate the feature, export the following variables or add them to a .env file:
ENABLE_PROJECT_THREADS=true
PROJECT_THREADS_MODE=group
PROJECT_THREADS_CHAT_ID=-1001234567890
PROJECTS_CONFIG_PATH=/home/bot/projects.yaml
ENABLE_PROJECT_THREADS: Boolean flag that activates the entire subsystem.PROJECT_THREADS_MODE: Must be eitherprivate(creates topics in private chats) orgroup(creates forum topics inside a specified group).PROJECT_THREADS_CHAT_ID: Required only when mode isgroup; specifies the Telegram forum chat ID.PROJECTS_CONFIG_PATH: Absolute path to the YAML file listing projects.
The Settings class validates cross-field dependencies (lines 216–228), raising ValueError if project_threads_chat_id is missing in group mode or if projects_config_path is absent when threading is enabled.
Defining Projects in the Registry
YAML Configuration Schema
Create the YAML file referenced by PROJECTS_CONFIG_PATH. The schema is validated by load_project_registry in src/projects/registry.py (lines 42–66):
projects:
- slug: api
name: API Service
path: api
enabled: true
- slug: web
name: Web Frontend
path: web
enabled: true
- slug: utils
name: Utility Library
path: utils
enabled: false
Each entry requires:
slug: Unique identifier used internally for routing.name: Human-readable label used as the Telegram topic title.path: Directory path relative to the approved root.enabled: Boolean controlling whether the project receives a topic.
Path Validation Rules
The registry loader enforces security constraints (lines 81–99 in src/projects/registry.py):
- Paths must be relative; absolute paths raise
ValueError. - Resolved paths must remain within the
approved_directoryto prevent directory traversal. - The directory must exist on disk.
Initializing the Thread Manager
Wire the components during bot startup. The ProjectThreadManager class in src/projects/thread_manager.py orchestrates topic lifecycle management:
from src.config.loader import load_settings
from src.projects.registry import load_project_registry
from src.projects.thread_manager import ProjectThreadManager
from src.storage.repositories import ProjectThreadRepository
settings = load_settings()
registry = load_project_registry(
config_path=settings.projects_config_path,
approved_directory=settings.approved_directory,
)
repo = ProjectThreadRepository()
thread_manager = ProjectThreadManager(registry, repo)
# Expose to handlers via bot_data
app.bot_data["thread_manager"] = thread_manager
app.bot_data["settings"] = settings
The ProjectThreadRepository (defined in src/storage/repositories.py) persists mappings between project slugs and Telegram message_thread_id values using SQLAlchemy models from src/storage/models.py.
Synchronizing Telegram Topics
The Sync Process
Invoke sync_topics to reconcile the Telegram forum state with the project registry. This method (lines 45–84 in src/projects/thread_manager.py) performs four operations:
- Creation: For each enabled project without an existing mapping, calls
bot.create_forum_topicand stores the returnedmessage_thread_id. - Reuse: Existing valid mappings are preserved to maintain conversation history.
- Renaming: If a project's
namechanged in the YAML but the topic exists, updates the topic title viabot.edit_forum_topic. - Closure: Identifies stale mappings (projects disabled or removed) and closes those topics.
Handling Stale Topics
The manager queries stale mappings through repository.list_stale_active_mappings (referenced at lines 97–100) and closes them gracefully:
# Simplified excerpt from thread_manager.py lines 97-102
stale_mappings = await self.repository.list_stale_active_mappings(
chat_id=chat_id,
active_project_slugs=active_slugs,
)
for stale in stale_mappings:
await bot.close_forum_topic(chat_id=chat_id, message_thread_id=stale.thread_id)
The implementation handles PrivateTopicsUnavailableError for private chat topics that may become inaccessible.
Storage and Persistence
Topic mappings are stored in the ProjectThreadModel SQLAlchemy class (defined in src/storage/models.py). The repository layer in src/storage/repositories.py provides async CRUD operations:
get_by_project_and_chat: Retrieves existing thread mapping.create_mapping: Stores new topic associations.list_stale_active_mappings: Finds orphaned mappings for cleanup.
This persistence ensures that bot restarts maintain consistent topic routing without recreating existing conversations.
Summary
- Enable the subsystem by setting
ENABLE_PROJECT_THREADS=trueand choosing aPROJECT_THREADS_MODE(privateorgroup). - Validate dependencies through the Pydantic
Settingsclass insrc/config/settings.py, which enforces required fields likePROJECT_THREADS_CHAT_IDfor group mode. - Define projects in a YAML file loaded by
src/projects/registry.py, ensuring paths are relative and contained within the approved directory. - Initialize the manager by wiring
ProjectThreadManagerwith aProjectThreadRepositoryduring bot startup. - Synchronize topics via
sync_topicsto create, rename, and close forum topics based on the current project registry state.
Frequently Asked Questions
What is the difference between private and group thread modes?
Private mode creates individual topics within private chats between the bot and each user, suitable for personal project management. Group mode creates forum topics inside a designated Telegram group chat (supergroup with forums enabled), allowing team-wide project discussions. Group mode requires the PROJECT_THREADS_CHAT_ID environment variable, while private mode uses the incoming chat ID dynamically.
How does the bot handle renamed or deleted projects?
The ProjectThreadManager.sync_topics method in src/projects/thread_manager.py detects configuration drift by comparing the active project registry against persisted mappings. When a project is renamed, the manager calls bot.edit_forum_topic to update the topic title. When a project is disabled or removed, the manager identifies stale mappings via list_stale_active_mappings and closes those topics using bot.close_forum_topic, ensuring no orphaned conversations remain.
Can I use absolute paths in the projects.yaml file?
No, absolute paths are explicitly rejected by the validation logic in src/projects/registry.py (lines 81–83). The loader requires relative paths to prevent directory traversal attacks and ensure all project directories remain within the APPROVED_DIRECTORY root. If an absolute path is provided, the bot raises a ValueError during startup, preventing the registry from loading.
What happens if the forum chat ID is invalid?
If PROJECT_THREADS_MODE is set to group but PROJECT_THREADS_CHAT_ID is missing, the Pydantic validator in src/config/settings.py (lines 216–224) raises a ValueError immediately upon settings load, preventing the bot from starting. If the chat ID is provided but invalid (e.g., the bot lacks permissions or the chat doesn't exist), Telegram API calls like create_forum_topic will raise a TelegramError during the sync_topics execution, which the handler should catch and log appropriately.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →