Instructions for Claude Code when working with the TG Support Bot project.
IMPORTANT: This project has a structured
rules/documentation system. Before starting any non-trivial task, readrules/README.mdand the relevant domain/process files listed below.
The rules/ directory is the source of truth for all architectural decisions, business rules, and coding standards.
Always read before working:
| Task Type | Files to Read |
|---|---|
| Any task | rules/README.md |
| Messaging / sending | rules/domain/messaging.md |
| User management / banning | rules/domain/bot-users.md |
| AI assistant logic | rules/domain/ai-assistant.md |
| External API integration | rules/domain/external-sources.md |
| Admin panel / manager UI | rules/domain/admin-panel.md |
| Database / migrations | rules/database/schema.md |
| HTTP routes / endpoints | rules/api/endpoints.md |
| Architecture decisions | rules/process/architecture-design.md |
| Logging / monitoring | rules/process/observability.md |
| Security / auth | rules/process/security.md |
| Tests | rules/process/testing-strategy.md |
| CI/CD / git hooks | rules/process/ci-cd.md |
TG Support Bot is a Laravel 12 application for customer support via Telegram and VK. The support team works in a Telegram supergroup with forum topics — each user gets their own topic thread. The project also integrates with external third-party systems via REST API.
Supported platforms:
- Telegram — main support channel (forum topics in supergroup)
- VK — secondary support channel
- External Sources — third-party integrations via REST API + webhooks
Key integrations:
- AI providers: OpenAI, DeepSeek, GigaChat (draft responses for manager review)
- Monitoring: Grafana + Loki + Sentry
- Live chat: Node.js server (port 3001)
| Component | Technology |
|---|---|
| Language | PHP 8.2+ |
| Framework | Laravel 12 |
| Database | PostgreSQL |
| Cache / Queue | Redis + Laravel Queue |
| Containers | Docker |
| API Documentation | L5-Swagger (annotations-based) |
| Static Analysis | PHPStan level 6 (larastan) |
| Code Formatting | Laravel Pint (PSR-12 + Laravel) |
| Testing | PHPUnit 11 + Mockery |
| Admin Panel | Filament 3 |
| Error Tracking | Sentry |
| Log Aggregation | Loki + Grafana + Promtail |
| Telegram Logging | prog-time/tg-logger |
HTTP Layer app/Http/Controllers/ + app/Modules/*/Controllers/
↓
DTO Layer app/DTOs/ + app/Modules/*/DTOs/
↓
Business Logic app/Services/ + app/Modules/*/Services/ + app/Actions/
↓ ↓
Integration ManagerInterfaceContract
app/Modules/Telegram/Api/ / \
app/Modules/Vk/Api/ TelegramGroupInterface AdminPanelInterface
↓ (forum topics) (Filament web panel)
Queue Layer app/Modules/*/Jobs/
↓
Data Layer app/Models/ + PostgreSQL
| Layer | Directory | Rule |
|---|---|---|
| Controllers | app/Http/Controllers/, app/Modules/*/Controllers/ |
Thin — receive request, dispatch job or call service, return response |
| Middleware | app/Modules/*/Middleware/ |
Validate incoming webhooks (Telegram, VK, External API auth) |
| DTOs | app/DTOs/, app/Modules/*/DTOs/ |
Parse and type incoming data via static fromRequest() |
| Services | app/Services/, app/Modules/*/Services/ |
Reusable business logic |
| Actions | app/Actions/, app/Modules/*/Actions/ |
Single isolated operations (one action = one thing) |
| Telegram/VK API | app/Modules/Telegram/Api/, app/Modules/Vk/Api/ |
Direct API calls only |
| Admin | app/Modules/Admin/ |
Filament resources, Livewire pages, SendReplyAction |
| Jobs | app/Modules/*/Jobs/ |
All async operations — message sending, webhooks |
| Models | app/Models/ |
Data operations only, no business logic, no API calls |
- Action Pattern —
app/Modules/*/Actions/— staticexecute(), one responsibility - Service Pattern —
app/Services/,app/Modules/*/Services/— injected, reusable logic - DTO Pattern —
app/DTOs/,app/Modules/*/DTOs/— typed data transfer between layers - Queue Pattern — all Telegram/VK API sends go through Jobs, never synchronously
- Middleware Pattern — webhook validation before controller runs
- Contract Pattern —
ManagerInterfaceContractdecouples manager UI from business logic
app/
├── Actions/ # Shared isolated operations (Ai/)
├── Contracts/ # Interfaces (AiProviderInterface, ManagerInterfaceContract)
├── DTOs/ # Shared Data Transfer Objects (Ai/, Button/, Redis/)
├── Enums/ # Enumerations (ButtonType, TelegramError, VkError)
├── Helpers/ # Utilities (TelegramHelper, AiHelper, DateHelper)
├── Http/
│ └── Controllers/ # SimplePage, FilesController, SwaggerController, PreviewController
├── Logging/ # LokiHandler
├── Models/ # BotUser, Message, ExternalMessage, ExternalSource, AiMessage, etc.
├── Modules/
│ ├── Admin/ # Filament 3 admin panel
│ │ ├── Actions/ # SendReplyAction
│ │ ├── Filament/
│ │ │ ├── Pages/ # ConversationPage (Livewire)
│ │ │ └── Resources/ # ConversationResource, BotUserResource, ExternalSourceResource
│ │ └── Services/ # AdminPanelInterface (ManagerInterfaceContract implementation)
│ ├── External/ # External Sources integration
│ │ ├── Actions/, Controllers/, DTOs/, Jobs/, Middleware/, Services/
│ ├── Telegram/ # Telegram bot
│ │ ├── Actions/, Api/, Controllers/, DTOs/, Jobs/, Middleware/, Services/
│ │ └── Services/TelegramGroupInterface.php # ManagerInterfaceContract implementation
│ └── Vk/ # VK bot
│ ├── Actions/, Api/, Controllers/, DTOs/, Jobs/, Middleware/, Services/
├── Providers/ # AppServiceProvider (binds ManagerInterfaceContract)
└── Services/ # Shared services (Ai/, Button/, File/, Swagger/, Webhook/)
docker compose up -d
docker exec -it pet composer installdocker exec -it pet ./vendor/bin/pintdocker exec -it pet ./vendor/bin/phpstan analysedocker exec -it pet php artisan test
# or
docker exec -it pet ./vendor/bin/phpunitdocker exec -it pet php artisan test --filter=TestName- Indentation: 4 spaces
- Single quotes for strings
- Short array syntax
[] - Trailing comma in multiline arrays
- Remove unused imports
- Sort imports alphabetically
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase |
SendBannedMessage |
| Methods, variables | camelCase |
getByTopicId() |
| Constants | UPPER_SNAKE_CASE |
MAX_RETRIES |
| Migration files | snake_case |
create_bot_users_table |
| Actions | Static execute() |
GetChat::execute($chatId) |
| DTOs | Static fromRequest() |
TelegramUpdateDto::fromRequest($request) |
| Jobs | *Job suffix |
SendTelegramMessageJob |
/**
* Brief method description.
*
* @param BotUser $botUser The target bot user
* @return TelegramAnswerDto
* @throws TelegramException When Telegram API call fails
*/
public static function execute(BotUser $botUser): TelegramAnswerDto
{
}- All message sending to Telegram/VK must go through queue Jobs, never synchronously
- Every sent/received message must be saved to the
messagestable - Banned users receive a banned notification, not a regular reply
- Each bot user has exactly one Telegram forum topic thread
- Every interaction creates or finds a
BotUserrecord - Users are identified by
chat_id+platform(notchat_idalone) - Banning sets
is_banned = true,banned_at, and closes the Telegram topic
- AI is disabled by default (
AI_ENABLED=false) - AI drafts must be reviewed and accepted/cancelled by a manager before sending (unless
AI_AUTO_REPLY=true) - Supported providers: OpenAI, DeepSeek, GigaChat (set via
AI_DEFAULT_PROVIDER)
- Requests must be authenticated with a bearer token from
external_source_access_tokens - When the team replies to an external user, a webhook is sent to
external_sources.webhook_url
MANAGER_INTERFACE=telegram_group(default) — managers work via Telegram supergroup with forum topicsMANAGER_INTERFACE=admin_panel— managers work via the/adminweb panel (Filament 3)- Switching: change
.env+ restart thephp-fpmcontainer (docker compose restart app) - Does not require
php artisan migrateor any DB changes - See
rules/domain/admin-panel.mdfor full rules anddocs/switching-manager-interface.mdfor runbook
- All Telegram webhooks validated by
TelegramQuerymiddleware (X-Telegram-Bot-Api-Secret-Token) - All VK webhooks validated by
VkQuerymiddleware - All External API requests validated by
ApiQuerymiddleware (bearer token) - Never pass raw
Requestobjects to Services or Actions — use DTOs - Never use raw SQL string concatenation — use Eloquent / query builder
- Never commit
.envfiles or hardcode secrets - Never log tokens, passwords, or API keys
issues-{number} | {brief description}
issues-123 | add VK sticker support
issues-45 | fix telegram webhook error handling
issues-78 | update rules documentation
add— new featurefix— bug fixupdate— update existing functionalityrefactor— refactoring (no behavior change)remove— deletiondocs— documentation onlytest— tests onlystyle— formatting onlychore— routine maintenance
issues-{number}
issues-{number}-{brief-description}
Examples: issues-38, issues-45-fix-telegram-webhook
Test file location mirrors source file location:
| Source | Test |
|---|---|
app/Actions/Telegram/GetChat.php |
tests/Unit/Actions/Telegram/GetChatTest.php |
app/Services/Tg/TgMessageService.php |
tests/Unit/Services/Tg/TgMessageServiceTest.php |
- New functionality must be covered by tests before merge
- Bug fixes must include a regression test
- Unit tests use
Http::fake()— no real API calls - Feature tests use
RefreshDatabasetrait - Test naming:
test_can_do_somethingortest_throws_exception_when_invalid - Tests run with SQLite in-memory database (see
phpunit.xml)
tests/
├── Unit/ # Actions, Services, Helpers, Models
├── Feature/ # HTTP endpoints, integrations
├── Mocks/ # Mock objects
├── Stubs/ # Raw data stubs (webhook payloads)
└── Traits/ # Reusable traits
| Hook | Script | What it checks |
|---|---|---|
pre-commit |
scripts/pre-commit-check.sh |
Laravel Pint formatting |
pre-push |
scripts/pre-push-check.sh |
PHPStan level 6 + PHPUnit |
Never bypass hooks with --no-verify.
Before marking any task complete:
- All new public methods have PHPDoc with type hints
- New classes have corresponding test files in
tests/ - Laravel Pint passes with no changes needed
- PHPStan level 6 passes with 0 errors
- All tests pass
- If schema changed →
rules/database/schema.mdupdated - If routes changed →
rules/api/endpoints.mdupdated - If business rules changed → relevant
rules/domain/*.mdupdated - No secrets committed (
.envexcluded from git)