A TypeScript library for building Telegram bots on Cloudflare Workers, providing a modern, type-safe, and developer-friendly framework for serverless bot development.
- Features
- Installation
- Basic Usage
- Bot Workflow
- Filter System
- Bot Instance Methods and Properties
- Wrappers
- Markup Types
- Context Types
- Support This Project
- License
- TypeScript-first design with comprehensive type definitions
- Serverless webhook-based architecture optimized for Cloudflare Workers
- Advanced context management and update handling
- Specialized context for chat member updates (joins, leaves)
- Comprehensive filter system for precise handler targeting
npm install workergramHere is a complete example of implementing a Telegram bot in a Cloudflare Worker using WorkerGram:
import { Bot, filters } from 'workergram';
// Your bot token should be stored in a Cloudflare Workers secret
export default {
async fetch(request, env, ctx) {
// Create a new bot instance using the environment variable
const bot = new Bot(env.TELEGRAM_BOT_TOKEN);
// Register command handlers - new simplified syntax
bot.onCommand('start', async (ctx) => {
await ctx.reply('Hello! I am a Telegram bot powered by WorkerGram.');
});
bot.onCommand('help', async (ctx) => {
await ctx.reply('This is a help message. You can use the following commands:\n' +
'/start - Start the bot\n' +
'/help - Show this help message');
});
// Handle regular text messages
bot.onUpdate('message', async (ctx) => {
if (ctx.message.text) {
await ctx.reply(`You said: ${ctx.message.text}`);
}
}, filters.custom(update =>
'message' in update &&
update.message?.text &&
!update.message.text.startsWith('/')
));
// Handle new members joining
bot.onUpdate('chat_member', async (ctx) => {
if (ctx.isJoining()) {
await ctx.reply(`Welcome to the group, ${ctx.user.displayName}!`);
}
}, filters.memberStatusChange('join'));
// If this is a POST request from Telegram, process it as an update
if (request.method === 'POST') {
try {
// Parse the request body as JSON
const update = await request.json();
// Process the update
await bot.processUpdate(update);
// Return a 200 OK response to Telegram
return new Response('OK', { status: 200 });
} catch (error) {
// Handle any errors
console.error('Error processing update:', error);
return new Response('Error processing update', { status: 500 });
}
}
// For all other requests, return a simple response
return new Response('WorkerGram Bot is running!', { status: 200 });
}
};The typical workflow of a bot follows these steps:
-
Create a Bot Instance
const bot = new Bot(env.BOT_TOKEN);
-
Declare Handlers for Events
// Use onUpdate for handling update types bot.onUpdate('message', (ctx) => { ctx.reply('Hello!'); }); bot.onUpdate('callback_query', (ctx) => { ctx.answer('Button clicked!'); }); // Use onCommand for handling commands (recommended) bot.onCommand('start', (ctx) => { ctx.reply('Bot started!'); });
-
Provide and Process the Update
// Get update from Telegram webhook const update = await request.json(); // Process the update await bot.processUpdate(update);
WorkerGram includes a powerful filtering system to help you handle exactly the updates you want. Here's a comprehensive guide to the available filters:
| Filter | Description | Example |
|---|---|---|
text |
Match exact message text | filters.text('hello') |
textMatches |
Match message text with regex | filters.textMatches(/^hi/i) |
callbackData |
Match exact callback query data | filters.callbackData('btn_1') |
callbackDataMatches |
Match callback data with regex | filters.callbackDataMatches(/^btn_/) |
command |
Match bot commands | filters.command('start') |
chatType |
Match specific chat types: private, group, supergroup, channel |
filters.chatType('private') |
chatId |
Match specific chat ID | filters.chatId(123456789) |
userId |
Match specific user ID | filters.userId(123456789) |
newChatMembers |
Match new chat member updates | filters.newChatMembers() |
leftChatMember |
Match left chat member updates | filters.leftChatMember() |
memberStatusChange |
Match specific member status changes: join, leave, promote, demote |
filters.memberStatusChange('join') |
| Operator | Description | Example |
|---|---|---|
and |
Combine filters with AND logic | filters.and([filters.chatType('private'), filters.textMatches(/hello/i)]) |
or |
Combine filters with OR logic | filters.or([filters.command('start'), filters.command('help')]) |
not |
Invert a filter | filters.not(filters.textMatches(/hello/i)) |
You can create custom filters for complex matching requirements:
// Match messages containing photos
bot.onUpdate('message', ctx => {
ctx.reply('Nice photo!');
}, filters.custom(update => {
return 'message' in update &&
update.message &&
'photo' in update.message &&
Array.isArray(update.message.photo) &&
update.message.photo.length > 0;
}));
// Match messages from a specific user
bot.onUpdate('message', ctx => {
ctx.reply('Message from admin');
}, filters.custom(update => {
if ('message' in update && update.message?.from) {
return update.message.from.id === 123456789; // Replace with actual admin ID
}
// Using the built-in userId filter is easier and more reliable
bot.onUpdate("message", ctx => {
ctx.reply("Message from admin using userId filter");
}, filters.userId(123456789)); // Replace with actual admin ID
// Filter messages from a specific chat
bot.onUpdate("message", ctx => {
ctx.reply("Message from an important group");
}, filters.chatId(-100123456789)); // Replace with actual group chat ID
return false;
return false;
}));The Bot class provides a comprehensive set of methods for interacting with the Telegram Bot API.
Handler Registration:
onCommand(command, handler, filter?): Register a handler for a specific commandonUpdate(event, handler, filter?): Register a handler for an update typeprocessUpdate(update): Process a Telegram update objectcallApi(method, params?): Call the Telegram API directly
Messaging Methods:
sendMessage(chatId, text, options?): Send a text message to a chat
Media Methods:
All media sending methods (sendPhoto, sendVideo, sendSticker, sendAudio, sendDocument) accept a MediaInput parameter.
sendPhoto(chatId, photo, options?): Send a photo to a chatsendVideo(chatId, video, options?): Send a video to a chatsendSticker(chatId, sticker, options?): Send a sticker to a chatsendAudio(chatId, audio, options?): Send an audio file to a chatsendDocument(chatId, document, options?): Send a document to a chat
Message Management Methods:
forwardMessage(chatId, fromChatId, messageId, options?): Forward a messagecopyMessage(chatId, fromChatId, messageId, options?): Copy a messagedeleteMessage(chatId, messageId): Delete a message
Interactive Methods:
answerCallbackQuery(callbackQueryId, options?): Answer a callback query
Chat Management Methods:
banChatMember(chatId, userId, untilDate?, revokeMessages?): Ban a user from a chatunbanChatMember(chatId, userId, onlyIfBanned?): Unban a userrestrictChatMember(chatId, userId, permissions, untilDate?): Restrict a userpromoteChatMember(chatId, userId, options?): Promote a usersetChatAdministratorCustomTitle(chatId, userId, customTitle): Set custom title
Forum Topic Management Methods:
createForumTopic(chatId, name, options?): Create a forum topiceditForumTopic(chatId, messageThreadId, options): Edit a forum topiccloseForumTopic(chatId, messageThreadId): Close a forum topicreopenForumTopic(chatId, messageThreadId): Reopen a forum topicdeleteForumTopic(chatId, messageThreadId): Delete a forum topicunpinAllForumTopicMessages(chatId, messageThreadId): Unpin all messageshideGeneralForumTopic(chatId): Hide the general forum topicunhideGeneralForumTopic(chatId): Unhide the general forum topicgetForumTopicIconStickers(): Get available forum topic icon stickers
Webhook Methods:
setWebhook(url, options?): Set a webhook for updatesdeleteWebhook(dropPendingUpdates?): Delete the webhookgetWebhookInfo(): Get information about the webhook
Info Methods:
getMe(): Get information about the botgetChatMember(chatId, userId): Get info about a chat membergetChat(chatId): Get information about a chat
Description:
A wrapper class for Telegram messages returning MessageInstance when sending/editing messages to ensure consistent method chaining and helper functions.
Properties:
raw: The raw TelegramMessageobjectchatId: Chat identifiermessageId: Message identifier
Methods:
editText(text: string, options?: SendMessageOptions): Edit this message's text, returnsPromise<MessageInstance | boolean>.SendMessageOptionsincludesreply_markup?: ReplyMarkup.delete(): Delete this message, returnsPromise<MessageInstance>reply(text: string, options?: SendMessageOptions, asReply?: boolean): Reply to this message, returnsPromise<MessageInstance>.SendMessageOptionsincludesreply_markup?: ReplyMarkup.replyWithPhoto(photo: string, options?: SendPhotoOptions, asReply?: boolean): Send a photo in reply, returnsPromise<MessageInstance>.SendPhotoOptionsincludesreply_markup?: ReplyMarkup.replyWithDocument(document: string, options?: SendDocumentOptions, asReply?: boolean): Send a document in reply, returnsPromise<MessageInstance>.SendDocumentOptionsincludesreply_markup?: ReplyMarkup.forward(toChatId: number | string, options?: ForwardMessageOptions): Forward this message to another chat, returnsPromise<MessageInstance>copy(toChatId: number | string, options?: CopyMessageOptions): Copy this message to another chat, returnsPromise<MessageInstance>
Generic wrapper for building reply_markup payloads in messages.
Classes:
ReplyMarkup: supports inline_keyboard, keyboard, resize_keyboard, one_time_keyboard, selective, remove_keyboard, force_reply.InlineKeyboardButton: builder for inline keyboard buttons.KeyboardButton: builder for reply keyboard buttons.
WorkerGram provides specialized context classes for different update types, each with structured property groups. Here's a reference for each context type:
Base class for all contexts. All other context types extend this class.
Properties:
bot: The Bot instanceupdate: The raw update object from TelegramuserId: ID of the user who triggered the updateuser: Structured user information objectid: User IDfirstName: First namelastName: Last name (if available)fullName: Combined first and last nameusername: Username (if available)displayName: User's display name (full name with username)
Used for handling message updates.
Properties:
chatId: ID of the chat where the message was sentmessageId: ID of the messagetext: Text content of the messagechat: Structured chat information objectid: Chat IDtopicId: Topic ID for forum channels (if available)type: Chat type (private, group, supergroup, channel)title: Chat title (for groups, supergroups, channels)username: Chat username (if available)firstName: First name (for private chats)lastName: Last name (for private chats)isForum: Whether the chat is a forummessageThreadId: Message thread ID for forum topics
message: Structured message information objectid: Message IDtext: Message textcommand: Command, if the message contains onecommandPayload: Command payload (text after command)date: Message dateisEdited: Whether the message was editedtype: Type of message (text, photo, video, etc.)photo: Array of photo sizes (if message contains photo)video: Video information (if message contains video)audio: Audio information (if message contains audio)document: Document information (if message contains document)sticker: Sticker information (if message contains sticker)voice: Voice information (if message contains voice)videoNote: Video note information (if message contains video note)animation: Animation information (if message contains animation)caption: Caption text (for media messages)
- All properties from BaseContext
Methods:
reply(text: string, options?: SendMessageOptions, asReply?: boolean): Reply to the current message (with optional quoting).editText(text: string, options?: SendMessageOptions): Edit the text of the current message.deleteMessage(): Delete the current message.replyWithPhoto(photo: MediaInput, options?: SendPhotoOptions, asReply?: boolean): Send a photo in reply.replyWithVideo(video: MediaInput, options?: SendVideoOptions, asReply?: boolean): Send a video in reply.replyWithSticker(sticker: MediaInput, options?: SendStickerOptions, asReply?: boolean): Send a sticker in reply.replyWithAudio(audio: MediaInput, options?: SendAudioOptions, asReply?: boolean): Send an audio file in reply.replyWithDocument(document: MediaInput, options?: SendDocumentOptions, asReply?: boolean): Send a document in reply.forwardMessage(toChatId: number | string, options?: ForwardMessageOptions): Forward this message to another chat.copyMessage(toChatId: number | string, options?: CopyMessageOptions): Copy this message to another chat.getChat(): Get information about the chat.banChatMember(userId: number, untilDate?: number, revokeMessages?: boolean): Ban a user.unbanChatMember(userId: number, onlyIfBanned?: boolean): Unban a user.restrictChatMember(permissions: ChatPermissions, untilDate?: number): Restrict a user in the chat.isChatMemberOf(chatId: number | string): Check if the user is a member of another chat.getMediaFileId(): Get the file ID of the media in the message.getCaption(): Get the caption of the media message.getCaptionEntities(): Get the caption entities of the media message.
Used for handling callback query updates (button clicks).
Properties:
chatId: ID of the chat where the callback query came from (if available)messageId: ID of the message with the inline keyboard (if available)callbackData: Data attached to the callback buttonchat: Structured chat information object (if available)message: Structured message information object (if available)callback: Structured callback query information objectid: Callback query IDchatInstance: Chat instance stringdata: Callback datagameShortName: Game short name (for game buttons)inlineMessageId: Inline message ID (for inline keyboards)
- All properties from BaseContext
Methods:
answer(text?: string, options?: AnswerCallbackQueryOptions): Answer the callback queryreply(text: string, options?: SendMessageOptions, asReply?: boolean): Reply to the associated message (with optional quoting).editText(text: string, options?: SendMessageOptions): Edit the text of the associated message.editReplyMarkup(replyMarkup: ReplyMarkup, options?: SendMessageOptions): Edit the reply markupdeleteMessage(): Delete the associated messagebanChatMember(userId: number, untilDate?: number, revokeMessages?: boolean): Ban a userunbanChatMember(userId: number, onlyIfBanned?: boolean): Unban a userrestrictChatMember(permissions: ChatPermissions, untilDate?: number, chatId?: number): Restrict a user in the chatisChatMemberOf(chatId: number | string): Check if the user is a member of another chat
Used for handling chat member updates (joins, leaves, etc.).
Properties:
chatId: ID of the chat where the update occurredchat: Structured chat information objectid: Chat IDtype: Chat type (group, supergroup, channel)title: Chat title
memberUpdate: Structured member update information objectfrom: User who triggered the updateoldInfo: Previous chat member infonewInfo: New chat member infooldStatus: Previous statusnewStatus: New statusupdateType: Type of update (chat_member or my_chat_member)
- All properties from BaseContext
Methods:
isJoining(): Check if this is a new member joining the chatisLeaving(): Check if this is a member leaving the chatisPromoted(): Check if this is a member being promotedisDemoted(): Check if this is a member being demotedreply(text: string, options?: SendMessageOptions, asReply?: boolean): Send a message to the chat (with optional quoting).banChatMember(userId?: number, untilDate?: number, revokeMessages?: boolean): Ban the user from the chat (renamed from banUser())unbanChatMember(userId?: number, onlyIfBanned?: boolean): Unban the user (renamed from unbanUser())isChatMemberOf(chatId: number | string): Check if the user is a member of another chatrestrictChatMember(permissions: ChatPermissions, untilDate?: number, chatId?: number): Restrict a user
Used for handling edited message updates.
Properties:
chatId: ID of the chat where the message was editedmessageId: ID of the edited messagetext: Text content of the edited message (if available)chat: Structured chat information objectmessage: Structured message information object- All properties from BaseContext
Methods:
reply(text: string, options?: SendMessageOptions, asReply?: boolean): Reply to the edited message (with optional quoting).deleteMessage(): Delete the edited messageforwardMessage(toChatId: number | string, options?: ForwardMessageOptions): Forward this message to another chat, returnsPromise<MessageInstance>copyMessage(toChatId: number | string, options?: CopyMessageOptions): Copy this message to another chat, returnsPromise<{ message_id: number }>getChat(): Get information about the chatrestrictChatMember(permissions: ChatPermissions, untilDate?: number, chatId?: number): Restrict a user in the chat
Used for handling inline query updates from users typing @botname in a chat.
Properties:
query: The query text entered by the userinlineQuery: Structured inline query information objectid: Inline query IDquery: Query textoffset: Pagination offsetchatType: Type of chat where the query was made
- All properties from BaseContext
Methods:
answer(results: InlineQueryResult[], options?: AnswerInlineQueryOptions): Answer the inline query with resultsanswerWithResults(results: InlineQueryResult[], options?: AnswerInlineQueryOptions): Alias for answer()isChatMemberOf(chatId: number | string): Check if the user is a member of another chat- Helper methods for creating inline query results:
createArticleResult(id: string, title: string, description: string, text: string, options?: InlineQueryResultArticleOptions)createPhotoResult(id: string, photoUrl: string, thumbnailUrl: string, title?: string, options?: InlineQueryResultPhotoOptions)createDocumentResult(id: string, title: string, documentUrl: string, thumbnailUrl: string, options?: InlineQueryResultDocumentOptions)createVideoResult(id: string, title: string, videoUrl: string, thumbnailUrl: string, options?: InlineQueryResultVideoOptions)createLocationResult(id: string, title: string, latitude: number, longitude: number, options?: InlineQueryResultLocationOptions)
generateResultId(): Generate a unique ID for inline query results
This project is licensed under the MIT License.