This document describes the new modular architecture for the Telegram bot, implementing clean architecture principles with proper separation of concerns.
The bot has been refactored into a modular structure with clear boundaries:
save-message/
├── internal/
│ ├── services/ # Business logic layer
│ │ ├── message_service.go
│ │ ├── topic_service.go
│ │ └── ai_service.go
│ ├── handlers/ # Request handling layer
│ │ ├── message_handlers.go
│ │ └── callback_handlers.go
│ ├── router/ # Routing layer
│ │ └── dispatcher.go
│ ├── ai/ # AI integration
│ ├── database/ # Data persistence
│ └── config/ # Configuration
├── main.go # Original monolithic implementation
└── cmd/modular/main.go # New modular implementation
Each key behavior is implemented as a dedicated function with a single purpose:
DeleteMessage(chatID, messageID)- Deletes messages from TelegramCopyMessageToTopic(chatID, fromChatID, messageID, threadID)- Copies messages to topicsSendMessage(chatID, text, opts)- Sends messages with optionsEditMessageText(chatID, messageID, text, opts)- Edits existing messagesAnswerCallbackQuery(callbackID, opts)- Answers callback queries
GetForumTopics(chatID)- Retrieves all topics in a forumCreateForumTopic(chatID, name)- Creates new topicsTopicExists(chatID, topicName)- Checks if topic existsFindTopicByName(chatID, topicName)- Finds topics by name
SuggestFolders(ctx, messageText, existingFolders)- AI-powered folder suggestions
HandleStartCommand()- Handles /start commandHandleHelpCommand()- Handles /help commandHandleTopicsCommand()- Handles /topics commandHandleAddTopicCommand()- Handles /addtopic commandHandleBotMention()- Handles bot mentionsHandleNonGeneralTopicMessage()- Handles messages in non-General topicsHandleGeneralTopicMessage()- Handles messages in General topic
HandleNewTopicCreationRequest()- Handles new topic creation requestsHandleRetryCallback()- Handles retry button clicksHandleShowAllTopicsCallback()- Shows all existing topicsHandleCreateTopicMenuCallback()- Shows topic creation menuHandleTopicSelectionCallback()- Handles topic selectionHandleTopicNameEntry()- Handles topic name input
The Dispatcher routes incoming updates to appropriate handlers:
func (d *Dispatcher) HandleUpdate(update *gotgbot.Update) error {
if update.CallbackQuery != nil {
return d.callbackHandlers.HandleCallbackQuery(update)
}
if update.Message != nil {
return d.handleMessage(update)
}
return nil
}The dispatcher includes helper methods for routing decisions:
IsEditRequest(update)- Checks if message is an edit requestIsTopicSelection(update)- Checks if callback is topic selectionIsNewTopicPrompt(update)- Checks if waiting for topic nameIsMessageInGeneralTopic(update)- Checks if message is in General topic
- Services Layer: Pure business logic, no HTTP concerns
- Handlers Layer: Request/response handling, no business logic
- Router Layer: Pure routing logic, no business or HTTP concerns
- Main Layer: Only setup and orchestration
handlers/- Request handling and user interactionservices/- Business logic and external API callsai/- AI integrationutils/- Shared utilitiesrouter/- Request routing and dispatching
All messages, callbacks, and responses go through the Dispatcher, which routes to appropriate handlers.
MessageService: Handles all Telegram message operationsTopicService: Handles all topic/forum operationsAIService: Handles all AI-related operations
The new mainNew() function only handles setup and orchestration, with no business logic.
Every handler entry and exit is logged with context:
log.Printf("[MessageHandlers] Handling /start command: ChatID=%d", update.Message.Chat.Id)- Easy to add new interaction types (search, tagging, etc.)
- Modular structure allows independent testing
- Clear interfaces make extension straightforward
- No message or user data saved in storage
- All data stays within Telegram
- Clean separation prevents data leakage
- All text messages are centralized
- Easy to implement i18n in the future
- Consistent messaging across handlers
To use the new modular architecture:
-
Run the new implementation:
go run cmd/modular/main.go
-
Or build and run:
go build -o bot_modular cmd/modular/main.go ./bot_modular
The original main.go is preserved for reference. The new modular implementation can be gradually adopted:
- Phase 1: Run both implementations in parallel
- Phase 2: Switch to modular implementation
- Phase 3: Remove old implementation
Each service and handler can be tested independently:
// Test MessageService
messageService := services.NewMessageService(botToken, db)
err := messageService.DeleteMessage(chatID, messageID)
// Test MessageHandlers
messageHandlers := handlers.NewMessageHandlers(messageService, topicService, aiService)
err := messageHandlers.HandleStartCommand(update)
// Test Dispatcher
dispatcher := router.NewDispatcher(messageHandlers, callbackHandlers)
err := dispatcher.HandleUpdate(update)- Maintainability: Clear separation makes code easier to understand and modify
- Testability: Each component can be tested independently
- Scalability: Easy to add new features without affecting existing code
- Debugging: Clear logging and separation make issues easier to trace
- Reusability: Services can be reused across different handlers
- Future-Proof: Architecture supports easy extension and modification