@decogram/framework
v0.0.11
Published
Meta-based Telegram Bot Programming
Readme
Decogram Framework
Decogram is a meta-based framework for building Telegram bots with TypeScript. It’s designed to be declarative, modular, and developer-friendly, leveraging decorators, dependency injection, and Telegraf under the hood.
📚 Table of Contents
📘 Introduction
Decogram is a modern and declarative framework for Telegram bot development in TypeScript. It wraps around Telegraf and introduces decorators, dependency injection, and modular architecture to simplify and structure your bot logic.
🚀 Key Features
- ✅ Decorator-Driven configuration
- 🔧 Dependency Injection with lifecycle management
- 📦 Modular Handlers for commands, messages, and buttons
- 🔐 Middleware Support (global and scoped)
- 🧠 Hierarchical Session Contexts
- 🧰 Error Handling at method and class level
- 🖨️ Structured Logging via
pino - 🔄 Polling/Webhook update strategies
Getting Started
Prerequisites
- Node.js 20+
- npm or Yarn
- Telegram Bot Token from @BotFather
Project Setup
mkdir my-decogram-bot
cd my-decogram-bot
npm init -yInstallation
npm install @decogram/framework telegraf express pino pino-pretty reflect-metadata
npm install -D typescript @types/node resolve-tspathsTypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"baseUrl": "./",
"paths": {
"@handlers/*": ["src/handlers/*"],
"@services/*": ["src/services/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}Project Structure
my-decogram-bot/
├── src/
│ ├── handlers/
│ │ ├── message/
│ │ │ └── hello.handler.ts
│ │ └── error/
│ │ └── global.handler.ts
│ ├── middleware/
│ │ └── auth.middleware.ts
│ ├── services/
│ │ └── example.service.ts
│ ├── sessions/
│ │ └── user.context.ts
│ └── index.ts
├── package.json
├── tsconfig.jsonObtaining Your Bot Token
- Open Telegram.
- Search for
@BotFather. - Send
/newbotand follow the instructions. - Copy and store the token securely.
Running Your First Bot
npx tsc
npx resolve-tspaths --project tsconfig.json
node dist/index.jsCreate Initial Setup With CLI
npx @decogram/framework new my-new-projectIt'll create a new project with the initial setup for you in order to skip the manual installation process
🧠 Core Concepts
@TelegramMaster
Defines the bot's main configuration.
@TelegramMaster({
updatedFetchStrategy: {
type: "POLLING",
data: { botToken: "YOUR_BOT_TOKEN" }
},
messageListeners: [HelloHandler],
callbackQueryListeners: [],
sessionContexts: [],
middlewares: []
})
class Master {}@MessageHandler
Marks a class as a Handler of type MessageHandler:
@MessageHandler()
export class HelloHandler {
constructor(@SendMessage private readonly send: TSendMessage) {}
@OnCommand("/start")
start(@Message msg: Context["message"]) {
this.send("Hello, " + (msg?.from?.first_name ?? "Guest"));
}
}@CallbackHandler
Marks a class as a Handler of type CallbackHandler:
@CallbackHandler()
export class HelloHandler {
constructor(@SendMessage private readonly send: TSendMessage) {}
@OnClick("start")
start(@Message msg: Context["message"]) {
this.send("Hello, " + (msg?.from?.first_name ?? "Guest"));
}
}Message Handlers
@OnMessage(predicates?): Matches any message@OnCommand("/start"): Matches a specific command@OnAnything: Matches all messages (fallback)
Callback Query Handlers
@OnCallbackQuery(predicates?): Matches button callbacks@OnClick("button_data"): Exact match for button data
Context Predicates
const onlyHumans: ContextPredicate = ctx => !ctx.from?.is_bot;Use them in @OnMessage([predicate1, predicate2]).
Dependency Injection
Supports DI in classes and methods.
@Service()
export class MyService {
hello() {
return "Hello from service";
}
}
@MessageHandler()
export class SomeHandler {
constructor(@Autowired private readonly svc: MyService) {}
@OnCommand("/hi")
hi(@SendMessage send: TSendMessage) {
send(this.svc.hello());
}
}Parameter Injection
@SendMessage: Sends a message@Message: Gets the raw message@Session: Injects a session context
Middleware
@Middleware()
export class AuthMiddleware implements MiddlewareHandler {
async reject(ctx: Context): Promise<boolean> {
if (ctx.from?.id !== 123456789) {
ctx.reply("Not authorized.");
return true;
}
return false;
}
}Apply globally via @TelegramMaster or locally via @Handler.
Middleware Chaining
@Middleware({ next: RateLimitMiddleware })
export class LoggingMiddleware implements MiddlewareHandler {
async reject(ctx: Context) {
console.log("User:", ctx.from?.id);
return false;
}
}Session Contexts
@SessionContext
export class UserContext implements ISessionContext {
private userId!: number;
loadContext(ctx: Context) {
this.userId = ctx.from?.id ?? -1;
}
getUserId() {
return this.userId;
}
}Hierarchical Session Contexts
You can inject one session context into another:
@SessionContext
export class SettingsContext implements ISessionContext {
constructor(@Session private readonly user: UserContext) {}
loadContext(ctx: Context) {
const id = this.user.getUserId();
// Load user settings from DB...
}
}Error Handling
@ErrorHandler((ctx, err) => {
console.error("Error:", err);
ctx.reply("Something went wrong.");
})
@MessageHandler()
export class SafeHandler {
@OnCommand("/fail")
fail() {
throw new Error("Failure");
}
}Use @MethodErrorHandler() for method-specific errors.
Logging
Decogram uses pino. You can extend or replace loggers if needed. By default, all core events are logged.
Update Fetching Strategies
// Polling
type: "POLLING",
data: { botToken: "YOUR_TOKEN" }
// Webhook
type: "WEBHOOK",
data: {
botToken: "YOUR_TOKEN",
listenOnPort: 3000,
callbackAfterRegister: async (token, port) => {
await bot.telegram.setWebhook(`https://your-domain.com/telegram/${token}`);
}
}📦 Basic Example: The Hello Bot
import { bootstrap } from "decogram/core/bootstrap";
import { CallbackHandler, TelegramMaster } from "decogram/core/decorators/io/class";
import { OnCommand } from "decogram/core/decorators/io/method";
import { SendMessage } from "decogram/core/decorators/io/parameter";
import { TSendMessage } from "decogram/core/types";
import { Context } from "telegraf";
@MessageHandler()
class HelloHandler {
constructor(@SendMessage private readonly send: TSendMessage) {}
@OnCommand("/start")
start(@Message msg: Context["message"]) {
this.send("Hello, " + (msg?.from?.first_name ?? "Guest") + "!");
}
}
@TelegramMaster({
updatedFetchStrategy: {
type: "POLLING",
data: { botToken: "YOUR_BOT_TOKEN" }
},
messageListeners: [HelloHandler]
})
class Master {}
bootstrap(Master);📖 API Reference
| Decorator / Interface | Type | Description |
|-------------------------|------------|---------------------------------------------|
| @TelegramMaster | Class | Root bot configuration |
| @Handler | Class | Registers a handler |
| @OnMessage | Method | Handles messages with optional predicates |
| @OnCommand | Method | Handles /command messages |
| @OnAnything | Method | Catch-all message handler |
| @OnCallbackQuery | Method | Handles callback queries |
| @OnClick | Method | Handles inline button clicks |
| @SendMessage | Parameter | Injects reply method |
| @Message | Parameter | Injects raw message |
| @Session | Parameter | Injects session context |
| @Service | Class | Declares a DI service |
| @Autowired | Parameter | Injects a DI service |
| @Middleware | Class | Declares middleware |
| @ErrorHandler | Class | Class/Method-level error handling |
Happy coding with Decogram! 🚀
Want to contribute? Open a PR or star the project!
