https://github.com/byteholic/teleplate
Telegram Bot Template with Grammy & Hono
https://github.com/byteholic/teleplate
bot-template grammy grammyjs hono honojs telegram telegram-bot-template
Last synced: 6 days ago
JSON representation
Telegram Bot Template with Grammy & Hono
- Host: GitHub
- URL: https://github.com/byteholic/teleplate
- Owner: ByteHolic
- Created: 2023-09-03T19:43:43.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2025-07-08T10:35:48.000Z (8 months ago)
- Last Synced: 2025-07-12T01:31:51.378Z (8 months ago)
- Topics: bot-template, grammy, grammyjs, hono, honojs, telegram, telegram-bot-template
- Language: TypeScript
- Homepage:
- Size: 29.3 KB
- Stars: 14
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# 🤖 MTCute Modular Bot Template
Telegram bot template built with **mtcute**, **Bun**, **TypeScript**, and **Drizzle ORM**. Features a NestJS-inspired modular architecture with dependency injection, decorators, and type safety.
> 💡 Also check out [TelePlate](https://github.com/ByteHolic/TelePlate/tree/grammy-version) - Our grammY-based bot template!
***
## ✨ Features
- 🏗️ **Modular Architecture** - NestJS-style modules, services, and decorators
- 💉 **Dependency Injection** - Custom DI container with automatic resolution
- 🎨 **Decorator-Based Handlers** - Clean and intuitive update handling
- 🗃️ **Drizzle ORM** - Type-safe database operations with SQLite
- 📁 **Path Aliases** - Clean imports (`@core`, `@common`, `@database`, `@modules`)
- ⚡ **Bun Runtime** - Lightning-fast performance
- 🔧 **Full TypeScript** - End-to-end type safety
- 📊 **Built-in Logger** - Configurable logging system
- ⚙️ **Environment Validation** - Zod-based configuration validation
- 🎭 **30+ Event Types** - Complete mtcute API coverage
- 🔄 **Hot Reload** - Auto-restart in development mode
***
## 📦 Usage
Follow these steps to set up and run your bot:
### 1. Create a New Repository
Start by creating a new repository using this template. [Click here to create](https://github.com/ByteHolic/TelePlate/generate).
### 2. Environment Variables Setup
Create an environment variables file:
```bash
cp .env.example .env
```
Edit `.env` and set the required variables:
```env
API_ID=12345678
API_HASH=your_api_hash
BOT_TOKEN=your_bot_token
NODE_ENV=development
LOG_LEVEL=debug
SESSION_NAME=bot_session
DATABASE_URL=./bot-data/bot.db
```
### 3. Database Setup
Initialize the database:
```bash
bun run db:push
```
### 4. Launching the Bot
**Development Mode:**
```bash
# Install dependencies
bun install
# Start bot with hot reload
bun run dev
```
**Production Mode:**
```bash
# Install production dependencies only
bun install --production
# Set NODE_ENV to production in .env
# Then start the bot
bun run start
```
***
## 📝 List of Available Commands
- `bun run dev` — Start in development mode with hot reload
- `bun run start` — Start in production mode
- `bun run db:push` — Push database schema
- `bun run db:studio` — Open Drizzle Studio (database GUI)
- `bun run db:generate` — Generate migrations
***
## 🗂️ Directory Structure
```
project-root/
├── src/
│ ├── core/ # Core framework
│ │ ├── di/ # Dependency injection
│ │ │ ├── container.ts # DI container
│ │ │ └── metadata.ts # Metadata keys
│ │ ├── decorators/ # All decorators
│ │ │ ├── injectable.decorator.ts
│ │ │ ├── inject.decorator.ts
│ │ │ ├── module.decorator.ts
│ │ │ ├── update.decorators.ts # 30+ event decorators
│ │ │ ├── message.decorators.ts # Message filters
│ │ │ ├── callback.decorators.ts # Callback queries
│ │ │ ├── inline.decorators.ts # Inline mode
│ │ │ └── chat.decorators.ts # Chat events
│ │ ├── interfaces/
│ │ │ └── module.interface.ts
│ │ └── module-loader.ts # Module loader
│ ├── common/ # Common utilities
│ │ ├── config/ # Configuration
│ │ │ ├── env.schema.ts # Zod validation
│ │ │ ├── env.validator.ts
│ │ │ ├── env.service.ts
│ │ │ └── config.module.ts
│ │ └── logger/ # Logging system
│ │ ├── logger.service.ts
│ │ ├── logger.interface.ts
│ │ └── logger.module.ts
│ ├── database/ # Database layer
│ │ ├── schema/ # Drizzle schemas
│ │ │ ├── users.schema.ts
│ │ │ └── chats.schema.ts
│ │ ├── db.service.ts
│ │ └── database.module.ts
│ ├── modules/ # Bot modules
│ │ ├── user/ # User module
│ │ │ ├── user.service.ts # Business logic
│ │ │ ├── user.updates.ts # Update handlers
│ │ │ └── user.module.ts # Module definition
│ │ └── chat/ # Chat module
│ │ ├── chat.service.ts
│ │ ├── chat.updates.ts
│ │ └── chat.module.ts
│ ├── bot.module.ts # Root module
│ └── index.ts # Entry point
├── drizzle/ # Database migrations
├── drizzle.config.ts # Drizzle configuration
├── tsconfig.json # TypeScript config with paths
├── package.json
└── .env # Environment variables
```
***
## 🚀 Quick Start Example
### Create a New Module
```typescript
// src/modules/hello/hello.service.ts
import { Injectable, Inject } from '@core/decorators';
import { LOGGER } from '@common/logger/constants';
import type { ILogger } from '@common/logger/logger.interface';
@Injectable()
export class HelloService {
constructor(@Inject(LOGGER) private readonly logger: ILogger) {}
getGreeting(name: string): string {
this.logger.log(`Generating greeting for ${name}`);
return `Hello, ${name}! 👋`;
}
}
```
```typescript
// src/modules/hello/hello.updates.ts
import { Inject } from '@core/decorators';
import { OnCommand, OnText } from '@core/decorators';
import { TELEGRAM_CLIENT } from '@core/module-loader';
import { HelloService } from './hello.service';
import type { TelegramClient, Message } from '@mtcute/bun';
export class HelloUpdates {
constructor(
@Inject(HelloService) private readonly helloService: HelloService,
@Inject(TELEGRAM_CLIENT) private readonly client: TelegramClient
) {}
@OnCommand('hello')
async handleHello(msg: Message) {
const greeting = this.helloService.getGreeting(msg.sender.firstName);
await this.client.sendText(msg.chat.id, greeting);
}
@OnText(/hi|hey/i)
async handleGreeting(msg: Message) {
await this.client.sendText(msg.chat.id, '👋 Hi there!');
}
}
```
```typescript
// src/modules/hello/hello.module.ts
import { Module } from '@core/decorators';
import { HelloService } from './hello.service';
import { HelloUpdates } from './hello.updates';
import { LoggerModule } from '@common/logger/logger.module';
@Module({
imports: [LoggerModule],
providers: [HelloService],
updates: [HelloUpdates],
exports: [HelloService]
})
export class HelloModule {}
```
Register in `bot.module.ts`:
```typescript
import { HelloModule } from '@modules/hello/hello.module';
@Module({
imports: [
ConfigModule,
LoggerModule,
DatabaseModule,
HelloModule, // Add here
]
})
export class BotModule {}
```
***
## 🎨 Available Decorators
### Message Handlers
```typescript
@OnCommand('start') // Single command
@OnCommand(['help', 'about']) // Multiple commands
@OnText() // Any text message
@OnText('hello') // Text contains "hello"
@OnText(/pattern/i) // Regex pattern
@OnPhoto() // Photo messages
@OnVideo() // Video messages
@OnAudio() // Audio messages
@OnVoice() // Voice messages
@OnDocument() // Document messages
@OnSticker() // Stickers
@OnAnimation() // GIFs
@OnContact() // Contacts
@OnLocation() // Location
@OnPoll() // Polls
@OnDice() // Dice
```
### Update Handlers
```typescript
@OnNewMessage() // New message
@OnEditMessage() // Message edited
@OnDeleteMessage() // Message deleted
@OnMessageGroup() // Album/media group
@OnChatMemberUpdate() // Member status changed
@OnUserStatusUpdate() // User online/offline
@OnUserTyping() // User typing
@OnHistoryRead() // Messages read
@OnBotStopped() // Bot blocked by user
@OnPollUpdate() // Poll updated
@OnPollVote() // Poll vote
@OnStoryUpdate() // Story posted
@OnBotReactionUpdate() // Reaction added
```
### Callback & Inline
```typescript
@OnCallback() // Any callback query
@OnCallback('button_id') // Specific callback data
@OnCallback(/^action_/) // Regex pattern
@OnInline() // Any inline query
@OnInline('search') // Contains text
@OnChosenInline() // Inline result chosen
```
### Chat Events
```typescript
@OnNewChatMembers() // New members
@OnLeftChatMember() // Member left
@OnPinnedMessage() // Message pinned
@OnNewChatTitle() // Title changed
@OnNewChatPhoto() // Photo changed
```
***
## 🗃️ Database Operations
### Define Schema
```typescript
// src/database/schema/posts.schema.ts
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
content: text('content').notNull(),
userId: integer('user_id').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
});
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
```
### Use in Service
```typescript
import { eq } from 'drizzle-orm';
@Injectable()
export class PostService {
constructor(@Inject(DbService) private readonly db: DbService) {}
async createPost(data: NewPost) {
return await this.db.db.insert(posts).values(data).returning();
}
async getPost(id: number) {
return await this.db.db.query.posts.findFirst({
where: eq(posts.id, id),
});
}
}
```
***
## 🔧 Configuration
All configuration is done via environment variables validated with Zod:
```typescript
// src/common/config/env.schema.ts
export const envSchema = z.object({
API_ID: z.coerce.number().positive(),
API_HASH: z.string().min(1),
BOT_TOKEN: z.string().min(1),
// Add your custom variables
MY_VAR: z.string().default('default_value'),
});
```
Access in services:
```typescript
constructor(@Inject(ENV_SERVICE) private readonly env: EnvService) {
const apiId = this.env.apiId;
const custom = this.env.get('MY_VAR');
}
```
***
## 🚢 Deployment
### Using Docker
```dockerfile
FROM oven/bun:latest
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production
COPY . .
RUN bun run db:push
CMD ["bun", "run", "start"]
```
Build and run:
```bash
docker build -t telegram-bot .
docker run -d --env-file .env telegram-bot
```
### Using PM2
```bash
# Install PM2
bun add -g pm2
# Start
pm2 start bun --name "telegram-bot" -- run start
# Monitor
pm2 logs telegram-bot
pm2 monit
```
***
## 💡 Best Practices
1. **Keep modules focused** - One responsibility per module
2. **Use dependency injection** - Better testability and maintainability
3. **Leverage path aliases** - Keep imports clean (`@core`, `@common`, etc.)
4. **Log important events** - Use the logger service
5. **Validate all inputs** - Use Zod schemas
6. **Handle errors gracefully** - Wrap handlers in try-catch
7. **Type everything** - Take advantage of TypeScript
***
## 📚 Learn More
- [mtcute Documentation](https://mtcute.dev) - Telegram client library
- [Drizzle ORM](https://orm.drizzle.team) - TypeScript ORM
- [Bun](https://bun.sh) - JavaScript runtime
- [TelePlate](https://github.com/ByteHolic/TelePlate) - grammY-based alternative
***
## 🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing`)
5. Open a Pull Request
***
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
***
## 🙏 Acknowledgments
- [mtcute](https://github.com/mtcute/mtcute) - Telegram client library
- [NestJS](https://nestjs.com) - Architecture inspiration
- [Drizzle ORM](https://orm.drizzle.team) - Database toolkit
- [Bun](https://bun.sh) - Fast all-in-one runtime
***
## 📞 Support
If you like this project, please consider giving it a ⭐️ on [GitHub](https://github.com/ByteHolic/TelePlate)!
**Made with ❤️ by [ByteHolic](https://github.com/ByteHolic)**