├── .envrc ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── npm-publish.yml │ ├── github-publish.yml │ ├── build_pr.yml │ ├── eslint.yml │ └── codeql.yml ├── setup_hooks.sh ├── assets ├── logo.png └── screenshot.png ├── schema.sql ├── .prettierrc ├── docs ├── .nojekyll ├── assets │ ├── navigation.js │ ├── highlight.css │ └── search.js ├── hierarchy.html ├── types │ ├── TelegramInlineQueryType.html │ └── TelegramCommand.html ├── classes │ ├── Update.html │ ├── WebhookCommands.html │ ├── TelegramInlineQueryResult.html │ └── Webhook.html ├── index.html ├── interfaces │ ├── TelegramInputMessageContent.html │ ├── TelegramFrom.html │ └── TelegramPhotoSize.html └── modules.html ├── packages ├── main │ ├── vitest.config.js │ ├── src │ │ ├── types │ │ │ ├── TelegramInputMessageContent.ts │ │ │ ├── TelegramFrom.ts │ │ │ ├── TelegramPhotoSize.ts │ │ │ ├── TelegramCommand.ts │ │ │ ├── TelegramDocument.ts │ │ │ ├── TelegramMessageEntity.ts │ │ │ ├── TelegramInlineQuery.ts │ │ │ ├── TelegramInlineQueryType.ts │ │ │ ├── TelegramInlineQueryResult.ts │ │ │ ├── TelegramUser.ts │ │ │ ├── TelegramCallbackQuery.ts │ │ │ ├── PartialTelegramUpdate.ts │ │ │ ├── ChatPermissions.ts │ │ │ ├── TelegramChat.ts │ │ │ ├── TelegramInlineQueryResultArticle.ts │ │ │ ├── TelegramInlineQueryResultVideo.ts │ │ │ ├── TelegramInlineQueryResultPhoto.ts │ │ │ ├── TelegramUpdate.ts │ │ │ ├── TelegramMessage.ts │ │ │ └── TelegramBusinessMessage.ts │ │ ├── main.ts │ │ ├── types.ts │ │ ├── webhook.ts │ │ ├── telegram_bot.ts │ │ ├── telegram_execution_context.ts │ │ └── telegram_api.ts │ ├── test │ │ ├── tsconfig.json │ │ └── telegram_bot.spec.ts │ ├── eslint.config.js │ ├── tsconfig.json │ ├── package.json │ ├── README.md │ └── LICENSE └── worker │ ├── babel.config.json │ ├── test │ ├── tsconfig.json │ └── worker.spec.ts │ ├── vitest.config.js │ ├── esbuild.config.js │ ├── tsconfig.json │ ├── eslint.config.js │ └── package.json ├── .gitignore ├── .githooks └── pre-commit ├── vitest.toml ├── wrangler.toml ├── worker.capnp ├── flake.lock ├── package.json ├── Makefile ├── eslint.config.mjs ├── README.md ├── flake.nix └── LICENSE /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [codebam] 2 | -------------------------------------------------------------------------------- /setup_hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git config --local core.hooksPath .githooks/ 3 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebam/cf-workers-telegram-bot/HEAD/assets/logo.png -------------------------------------------------------------------------------- /schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS Messages (id TEXT PRIMARY KEY, userId TEXT, content TEXT); 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebam/cf-workers-telegram-bot/HEAD/assets/screenshot.png -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /packages/main/vitest.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | exclude: [], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInputMessageContent.ts: -------------------------------------------------------------------------------- 1 | interface TelegramInputMessageContent { 2 | message_text: string; 3 | parse_mode: string; 4 | } 5 | export default TelegramInputMessageContent; 6 | -------------------------------------------------------------------------------- /packages/worker/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env" 5 | ] 6 | ], 7 | "targets": { 8 | "browsers": "last 1 chrome version" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramFrom.ts: -------------------------------------------------------------------------------- 1 | interface TelegramFrom { 2 | first_name: string; 3 | id: number; 4 | is_bot: boolean; 5 | language_code: string; 6 | username: string; 7 | } 8 | export default TelegramFrom; 9 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramPhotoSize.ts: -------------------------------------------------------------------------------- 1 | interface TelegramPhotoSize { 2 | file_id: string; 3 | file_unique_id: string; 4 | width: number; 5 | height: number; 6 | file_size?: number; 7 | } 8 | export default TelegramPhotoSize; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | dist/ 3 | out/ 4 | **/*.rs.bk 5 | Cargo.lock 6 | bin/ 7 | pkg/ 8 | wasm-pack.log 9 | node_modules/ 10 | .cargo-ok 11 | *.swp 12 | Session.vim 13 | lerna-debug.log 14 | .tmp/ 15 | .wrangler/ 16 | .direnv/ 17 | result 18 | -------------------------------------------------------------------------------- /packages/main/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "bundler", 5 | "types": ["@cloudflare/workers-types/experimental"] 6 | }, 7 | "include": ["./**/*.ts", "../src/env.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if ! ( npm run build --workspace=packages && npm run lint --workspace=packages && CI=true npm run test --workspace=packages/main ) ; then 4 | echo "pre-push hook failed with exit status $?" 5 | exit 1 6 | fi 7 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramCommand.ts: -------------------------------------------------------------------------------- 1 | import TelegramBot from '../telegram_bot.js'; 2 | import TelegramUpdate from './TelegramUpdate.js'; 3 | 4 | type TelegramCommand = (bot: TelegramBot, update: TelegramUpdate, args: string[]) => Promise; 5 | export default TelegramCommand; 6 | -------------------------------------------------------------------------------- /packages/worker/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "bundler", 5 | "types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers"] 6 | }, 7 | "include": ["./**/*.ts", "../src/env.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /vitest.toml: -------------------------------------------------------------------------------- 1 | name = "cf-workers-telegram-bot-v2" 2 | main = "packages/worker/src/worker.ts" 3 | 4 | workers_dev = true 5 | compatibility_date = "2024-11-11" 6 | node_compat = true 7 | compatibility_flags = [ "nodejs_compat" ] 8 | 9 | build.command = "npm install && npm run build:worker" 10 | -------------------------------------------------------------------------------- /packages/worker/vitest.config.js: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | poolOptions: { 6 | workers: { 7 | wrangler: { configPath: '../../vitest.toml' }, 8 | }, 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramDocument.ts: -------------------------------------------------------------------------------- 1 | import TelegramPhotoSize from './TelegramPhotoSize.js'; 2 | 3 | export interface TelegramDocument { 4 | file_name: string; 5 | mime_type: string; 6 | thumbnail: TelegramPhotoSize; 7 | file_id: string; 8 | file_unique_id: string; 9 | file_size: number; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramMessageEntity.ts: -------------------------------------------------------------------------------- 1 | import TelegramUser from './TelegramUser.js'; 2 | 3 | interface TelegramMessageEntity { 4 | type: string; 5 | offset: number; 6 | length: number; 7 | url?: string; 8 | user?: TelegramUser; 9 | language?: string; 10 | } 11 | export default TelegramMessageEntity; 12 | -------------------------------------------------------------------------------- /packages/worker/esbuild.config.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import babel from 'esbuild-plugin-babel'; 3 | 4 | esbuild.build({ 5 | entryPoints: ['dist/worker/src/worker.js'], 6 | bundle: true, 7 | format: 'esm', 8 | minify: true, 9 | outfile: 'dist/worker/src/worker.mjs', 10 | plugins: [babel()], 11 | }); 12 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQuery.ts: -------------------------------------------------------------------------------- 1 | import TelegramFrom from './TelegramFrom.js'; 2 | 3 | interface TelegramInlineQuery { 4 | chat_type: 'sender' | 'private' | 'group' | 'supergroup' | 'channel'; 5 | from: TelegramFrom; 6 | id: number; 7 | offset: string; 8 | query: string; 9 | } 10 | export default TelegramInlineQuery; 11 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQueryType.ts: -------------------------------------------------------------------------------- 1 | type TelegramInlineQueryType = 2 | | 'article' 3 | | 'photo' 4 | | 'gif' 5 | | 'mpeg4_gif' 6 | | 'video' 7 | | 'audio' 8 | | 'voice' 9 | | 'document' 10 | | 'location' 11 | | 'venue' 12 | | 'contact' 13 | | 'game' 14 | | 'sticker'; 15 | export default TelegramInlineQueryType; 16 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQueryResult.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQueryType from './TelegramInlineQueryType.js'; 2 | 3 | export default class TelegramInlineQueryResult { 4 | type: TelegramInlineQueryType; 5 | id: string; 6 | constructor(type: TelegramInlineQueryType) { 7 | this.type = type; 8 | this.id = crypto.randomUUID(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramUser.ts: -------------------------------------------------------------------------------- 1 | interface TelegramUser { 2 | id: number; 3 | is_bot: boolean; 4 | first_name: string; 5 | last_name?: string; 6 | username?: string; 7 | language_code?: string; 8 | can_join_groups?: boolean; 9 | can_read_all_group_messages?: boolean; 10 | supports_inline_queries: boolean; 11 | } 12 | export default TelegramUser; 13 | -------------------------------------------------------------------------------- /wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "cf-workers-telegram-bot-v2" 2 | main = "packages/worker/src/index.ts" 3 | 4 | workers_dev = true 5 | compatibility_date = "2025-06-04" 6 | 7 | [ai] 8 | binding = "AI" 9 | 10 | [observability.logs] 11 | enabled = true 12 | 13 | [[d1_databases]] 14 | binding = "DB" 15 | database_name = "llama2" 16 | database_id = "e24eb37b-6b13-4e84-b1af-63de0dedcc6d" 17 | 18 | [[r2_buckets]] 19 | binding = "R2" 20 | bucket_name = "share" 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/packages/main" 5 | schedule: 6 | interval: "weekly" 7 | allow: 8 | - dependency-type: "all" 9 | versioning-strategy: increase 10 | 11 | - package-ecosystem: "npm" 12 | directory: "/packages/worker" 13 | schedule: 14 | interval: "weekly" 15 | allow: 16 | - dependency-type: "all" 17 | versioning-strategy: increase 18 | -------------------------------------------------------------------------------- /packages/main/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import eslint from '@eslint/js'; 3 | import tseslint from 'typescript-eslint'; 4 | 5 | export default [ 6 | { languageOptions: { globals: globals.browser } }, 7 | eslint.configs.recommended, 8 | ...tseslint.configs.strictTypeChecked, 9 | ...tseslint.configs.stylisticTypeChecked, 10 | { 11 | languageOptions: { 12 | parserOptions: { 13 | project: true, 14 | tsconfigRootDir: import.meta.dirname, 15 | }, 16 | }, 17 | }, 18 | ]; 19 | -------------------------------------------------------------------------------- /packages/worker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*"], 3 | "compilerOptions": { 4 | "strict": true, 5 | "target": "esnext", 6 | "module": "esnext", 7 | "lib": ["esnext"], 8 | "types": ["@cloudflare/workers-types"], 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "allowJs": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "declaration": true, 16 | "outDir": "dist" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/worker/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import eslint from '@eslint/js'; 3 | import tseslint from 'typescript-eslint'; 4 | 5 | export default [ 6 | { languageOptions: { globals: globals.browser } }, 7 | eslint.configs.recommended, 8 | ...tseslint.configs.strictTypeChecked, 9 | ...tseslint.configs.stylisticTypeChecked, 10 | { 11 | languageOptions: { 12 | parserOptions: { 13 | project: true, 14 | tsconfigRootDir: import.meta.dirname, 15 | }, 16 | }, 17 | }, 18 | ]; 19 | -------------------------------------------------------------------------------- /packages/main/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*"], 3 | "compilerOptions": { 4 | "strict": true, 5 | "noImplicitAny": false, 6 | "target": "esnext", 7 | "module": "esnext", 8 | "lib": ["esnext"], 9 | "types": ["@cloudflare/workers-types"], 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "allowJs": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "declaration": true, 17 | "outDir": "dist" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/assets/navigation.js: -------------------------------------------------------------------------------- 1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA53UTU/CQBAG4P9Sr0QBQYUbNph4MEGFeDAc1nagG/aj2Z0moPG/u5QCS2nXLdfOO8+022k/fwKENQbDIIYFyRgGrSAlmJgLXMYZA32dIGdXx+qKijgY9jqD3m2799s69E+BwVIRPkrp0YgY0Rr0jVXMvYPT6T5UGI8S6w1T9DHGa4gypFKEUuSlWrCc9NGfBaMCXjNQmzfQJ+dW5s+iF/kjhTRi0GBM0XHRtEkiUTaYled9Js3SmKDjKXZ1l1Qn/N/5AV+JlKvz1qLg0RtKzomIda2xD7isMCE4AcWp1mbpLIua9VMLEhmulDnluv07i5sQ86IJqzthC61Muuh9cns3laId8IGelOROaBvwgawNdHpWzo9NM3wBrckS8r+BcD93Rd5nTNHhpItMA24skKL7OE6SPnT+Yb/Tb/e9HlI+5EyDcmrbgNdW7r61o4Wb1N7JXbkktQf3nX7XvVFT49SppVi1Pv8DXCvn/lcHAAA=" -------------------------------------------------------------------------------- /packages/main/src/types/TelegramCallbackQuery.ts: -------------------------------------------------------------------------------- 1 | import TelegramFrom from './TelegramFrom.js'; 2 | import TelegramMessage from './TelegramMessage.js'; 3 | 4 | interface TelegramCallbackQuery { 5 | chat_type: 'sender' | 'private' | 'group' | 'supergroup' | 'channel'; 6 | from: TelegramFrom; 7 | id: number; 8 | offset: string; 9 | query: string; 10 | message: TelegramMessage, 11 | inline_message_id: string; 12 | chat_instance: string; 13 | data: string; 14 | game_short_name: string; 15 | } 16 | export default TelegramCallbackQuery; 17 | -------------------------------------------------------------------------------- /packages/main/src/types/PartialTelegramUpdate.ts: -------------------------------------------------------------------------------- 1 | import TelegramBusinessMessage from './TelegramBusinessMessage.js'; 2 | import TelegramInlineQuery from './TelegramInlineQuery.js'; 3 | import TelegramMessage from './TelegramMessage.js'; 4 | 5 | interface PartialTelegramUpdate { 6 | update_id?: number; 7 | message?: TelegramMessage; 8 | edited_message?: TelegramMessage; 9 | channel_post?: TelegramMessage; 10 | edited_channel_post?: TelegramMessage; 11 | inline_query?: TelegramInlineQuery; 12 | business_message?: TelegramBusinessMessage; 13 | } 14 | export default PartialTelegramUpdate; 15 | -------------------------------------------------------------------------------- /packages/main/src/types/ChatPermissions.ts: -------------------------------------------------------------------------------- 1 | interface ChatPermissions { 2 | can_send_messages?: boolean; 3 | can_send_audios?: boolean; 4 | can_send_documents?: boolean; 5 | can_send_photos?: boolean; 6 | can_send_videos?: boolean; 7 | can_send_video_notes?: boolean; 8 | can_send_voice_notes?: boolean; 9 | can_send_polls?: boolean; 10 | can_send_other_messages?: boolean; 11 | can_add_web_page_previews?: boolean; 12 | can_change_info?: boolean; 13 | can_invite_users?: boolean; 14 | can_pin_messages?: boolean; 15 | can_manage_topics?: boolean; 16 | } 17 | export default ChatPermissions; 18 | -------------------------------------------------------------------------------- /worker.capnp: -------------------------------------------------------------------------------- 1 | using Workerd = import "/workerd/workerd.capnp"; 2 | 3 | const config :Workerd.Config = ( 4 | services = [ 5 | (name = "main", worker = .worker), 6 | ], 7 | sockets = [ 8 | (service = "main", name = "http", address = "*:8080", http = ()), 9 | ] 10 | ); 11 | 12 | const worker :Workerd.Worker = ( 13 | modules = [ 14 | (name = "worker", esModule = embed "index.js"), 15 | ], 16 | compatibilityDate = "2024-11-11", 17 | bindings = [ 18 | ( 19 | name = "SECRET_TELEGRAM_API_TOKEN", 20 | text = "put your token here before building your docker image" 21 | ), 22 | ], 23 | ); 24 | 25 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1750506804, 6 | "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "4206c4cb56751df534751b058295ea61357bbbaa", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramChat.ts: -------------------------------------------------------------------------------- 1 | import TelegramMessage from './TelegramMessage.js'; 2 | 3 | interface TelegramChat { 4 | id: number; 5 | type: string; 6 | title?: string; 7 | username?: string; 8 | first_name?: string; 9 | last_name?: string; 10 | // photo?: TelegramChatPhoto; 11 | bio?: string; 12 | has_private_forwards: boolean; 13 | description?: string; 14 | invite_link?: string; 15 | pinned_message?: TelegramMessage; 16 | // permissions?: TelegramChatPermissions; 17 | slow_mode_delay?: number; 18 | message_auto_delete_time?: number; 19 | has_protected_content?: boolean; 20 | sticker_set_name?: string; 21 | can_set_sticker_set?: boolean; 22 | linked_chat_id?: number; 23 | } 24 | export default TelegramChat; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "author": "codebam", 5 | "license": "Apache-2.0", 6 | "scripts": { 7 | "build": "npm run build --workspaces", 8 | "build:worker": "npm run build --workspace=packages/worker", 9 | "test": "npm run test --workspaces", 10 | "test:worker": "npm run test --workspace=packages/worker", 11 | "test:main": "npm run test --workspace=packages/main", 12 | "lint": "npm run lint --workspaces", 13 | "ncu": "ncu --workspaces --root -u" 14 | }, 15 | "workspaces": [ 16 | "packages/main", 17 | "packages/worker" 18 | ], 19 | "devDependencies": { 20 | "@typescript-eslint/eslint-plugin": "^8.35.0", 21 | "npm-check-updates": "^18.0.1", 22 | "typedoc": "^0.28.6" 23 | }, 24 | "version": "" 25 | } 26 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQueryResultArticle.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQueryResult from './TelegramInlineQueryResult.js'; 2 | import TelegramInputMessageContent from './TelegramInputMessageContent.js'; 3 | 4 | export default class TelegramInlineQueryResultArticle extends TelegramInlineQueryResult { 5 | title: string; 6 | input_message_content: TelegramInputMessageContent; 7 | thumb_url: string; 8 | constructor(data: { content: string; title?: string; parse_mode?: string; thumb_url?: string }) { 9 | super('article'); 10 | this.title = data.title ?? ''; 11 | this.input_message_content = { 12 | message_text: data.content.toString(), 13 | parse_mode: data.parse_mode ?? '', 14 | }; 15 | this.thumb_url = data.thumb_url ?? ''; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/worker/test/worker.spec.ts: -------------------------------------------------------------------------------- 1 | import { env, createExecutionContext, waitOnExecutionContext } from 'cloudflare:test'; 2 | import { describe, it, expect } from 'vitest'; 3 | import worker, { Environment } from '../src/worker'; 4 | 5 | declare module 'cloudflare:test' { 6 | interface ProvidedEnv extends Environment {} 7 | } 8 | 9 | const IncomingRequest = Request; 10 | 11 | describe('telegram bot worker', () => { 12 | it('responds with ok', async () => { 13 | const request = new IncomingRequest('http://example.com'); 14 | const ctx = createExecutionContext(); 15 | const response = await worker.fetch(request, env, ctx); 16 | await waitOnExecutionContext(ctx); 17 | expect(await response.text()).toBe('ok'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQueryResultVideo.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQueryResult from './TelegramInlineQueryResult.js'; 2 | import TelegramInputMessageContent from './TelegramInputMessageContent.js'; 3 | 4 | export default class TelegramInlineQueryResultVideo extends TelegramInlineQueryResult { 5 | video_url: string; 6 | thumb_url: string; 7 | photo_width?: number; 8 | photo_height?: number; 9 | title?: string; 10 | description?: string; 11 | caption?: string; 12 | parse_mode?: string; 13 | caption_entities?: string; 14 | // reply_markup?: TelegramInlineKeyboardMarkup; 15 | input_message_content?: TelegramInputMessageContent; 16 | constructor(video: string) { 17 | super('video'); 18 | this.video_url = video; 19 | this.thumb_url = video; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramInlineQueryResultPhoto.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQueryResult from './TelegramInlineQueryResult.js'; 2 | import TelegramInputMessageContent from './TelegramInputMessageContent.js'; 3 | 4 | export default class TelegramInlineQueryResultPhoto extends TelegramInlineQueryResult { 5 | photo_url: string; // must be a jpg 6 | thumb_url: string; 7 | photo_width?: number; 8 | photo_height?: number; 9 | title?: string; 10 | description?: string; 11 | caption?: string; 12 | parse_mode?: string; 13 | caption_entities?: string; 14 | // reply_markup?: TelegramInlineKeyboardMarkup; 15 | input_message_content?: TelegramInputMessageContent; 16 | constructor(photo: string) { 17 | super('photo'); 18 | this.photo_url = photo; 19 | this.thumb_url = photo; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DATE=$(shell date +%Y%m%d) 2 | BUILD_ID=$(shell find packages/main/src packages/worker/src -type f ! -name '*.swp' -exec sha256sum {} + | LC_ALL=C sort | sha256sum | cut -d ' ' -f1) 3 | NPM_INSTALL=.tmp/build_$(BUILD_DATE) 4 | BUILD=.tmp/build_$(BUILD_ID) 5 | WORKER=.tmp/build_worker_$(BUILD_ID) 6 | 7 | all: build 8 | 9 | build: $(BUILD) 10 | worker: $(WORKER) 11 | npm_install: $(NPM_INSTALL) 12 | 13 | $(NPM_INSTALL): 14 | npm install 15 | mkdir -p .tmp; touch $(NPM_INSTALL) 16 | 17 | $(BUILD): $(NPM_INSTALL) 18 | npm run build 19 | mkdir -p .tmp; touch $(BUILD) 20 | 21 | $(WORKER): $(NPM_INSTALL) 22 | npm run build:worker 23 | mkdir -p .tmp; touch $(WORKER) 24 | 25 | release: $(NPM_INSTALL) 26 | npm run release 27 | 28 | .PHONY : clean 29 | clean : 30 | -rm -rf packages/main/dist 31 | -rm -rf packages/worker/dist 32 | -rm -rf .tmp/build_* 33 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: publish npm package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | npm-package: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: latest 15 | registry-url: https://registry.npmjs.org/ 16 | - id: node-modules-cache 17 | uses: actions/cache@v3 18 | with: 19 | path: | 20 | node_modules 21 | key: node-modules-${{ hashFiles('package-lock.json') }} 22 | restore-keys: node-modules- 23 | - run: npm i 24 | - run: npm ci 25 | - run: npm run lint --workspace=packages 26 | - run: npm run build --workspace=packages 27 | - run: npm publish --workspace=packages/main 28 | env: 29 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/github-publish.yml: -------------------------------------------------------------------------------- 1 | name: publish github package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish-github: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: read 12 | packages: write 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: latest 18 | registry-url: https://npm.pkg.github.com/ 19 | - id: node-modules-cache 20 | uses: actions/cache@v3 21 | with: 22 | path: | 23 | node_modules 24 | key: node-modules-${{ hashFiles('package-lock.json') }} 25 | restore-keys: node-modules- 26 | - run: npm i 27 | - run: npm ci 28 | - run: npm run lint --workspace=packages 29 | - run: npm run build --workspace=packages 30 | - run: npm publish --workspace=packages/main 31 | env: 32 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 33 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin"; 2 | import globals from "globals"; 3 | import tsParser from "@typescript-eslint/parser"; 4 | import path from "node:path"; 5 | import { fileURLToPath } from "node:url"; 6 | import js from "@eslint/js"; 7 | import { FlatCompat } from "@eslint/eslintrc"; 8 | 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = path.dirname(__filename); 11 | const compat = new FlatCompat({ 12 | baseDirectory: __dirname, 13 | recommendedConfig: js.configs.recommended, 14 | allConfig: js.configs.all 15 | }); 16 | 17 | export default [ 18 | ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"), 19 | { 20 | plugins: { 21 | "@typescript-eslint": typescriptEslint, 22 | }, 23 | 24 | languageOptions: { 25 | globals: { 26 | ...globals.worker, 27 | }, 28 | 29 | parser: tsParser, 30 | ecmaVersion: "latest", 31 | sourceType: "module", 32 | }, 33 | 34 | rules: { 35 | curly: "error", 36 | }, 37 | }, 38 | ]; -------------------------------------------------------------------------------- /packages/main/test/telegram_bot.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import TelegramBot from '../src/telegram_bot'; 3 | 4 | describe('telegram bot', () => { 5 | // Test for inline query handling 6 | it('inline response', async () => { 7 | const bot = new TelegramBot('123456789').on(':message', async () => { 8 | return Promise.resolve(new Response('ok')); 9 | }); 10 | const request = new Request('http://example.com/123456789', { 11 | method: 'POST', 12 | body: JSON.stringify({ inline_query: { query: 'hello' } }), 13 | }); 14 | expect(await (await bot.handle(request)).text()).toBe('ok'); 15 | expect(bot.currentContext.update_type).toBe('inline'); 16 | }); 17 | 18 | // Test for message handling 19 | it('message response', async () => { 20 | const bot = new TelegramBot('123456789').on(':message', async () => { 21 | return Promise.resolve(new Response('ok')); 22 | }); 23 | const request = new Request('http://example.com/123456789', { 24 | method: 'POST', 25 | body: JSON.stringify({ message: { text: 'hello' } }), 26 | }); 27 | expect(await (await bot.handle(request)).text()).toBe('ok'); 28 | expect(bot.currentContext.update_type).toBe('message'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codebam/cf-workers-telegram-bot", 3 | "version": "9.1.3", 4 | "description": "serverless telegram bot on cf workers", 5 | "main": "./dist/main.js", 6 | "module": "./dist/main.js", 7 | "types": "./dist/types.d.ts", 8 | "files": [ 9 | "dist", 10 | "LICENSE", 11 | "LICENSE_MIT", 12 | "README.md" 13 | ], 14 | "keywords": [ 15 | "cloudflare", 16 | "telegram", 17 | "worker", 18 | "webhook" 19 | ], 20 | "type": "module", 21 | "private": false, 22 | "scripts": { 23 | "build": "tsc --project tsconfig.json", 24 | "lint": "eslint src", 25 | "test": "vitest --config vitest.config.js" 26 | }, 27 | "author": "codebam", 28 | "license": "Apache-2.0", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/codebam/cf-workers-telegram-bot.git" 32 | }, 33 | "devDependencies": { 34 | "@cloudflare/workers-types": "^4.20250628.0", 35 | "@eslint/js": "^9.30.0", 36 | "@typescript-eslint/eslint-plugin": "^8.35.0", 37 | "@typescript-eslint/parser": "^8.35.0", 38 | "eslint": "^9.30.0", 39 | "eslint-config-prettier": "^10.1.5", 40 | "globals": "^16.2.0", 41 | "prettier": "^3.6.2", 42 | "typescript": "^5.8.3", 43 | "typescript-eslint": "^8.35.0", 44 | "vitest": "^3.2.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker", 3 | "version": "7.19.0", 4 | "private": true, 5 | "description": "serverless telegram bot on cf workers", 6 | "main": "./dist/worker.mjs", 7 | "module": "./dist/worker.mjs", 8 | "type": "module", 9 | "scripts": { 10 | "build": "esbuild ./src/index.ts --bundle --outfile=../../dist/index.js --platform=neutral --format=esm", 11 | "lint": "eslint src", 12 | "test": "vitest --config vitest.config.js" 13 | }, 14 | "author": "codebam", 15 | "license": "Apache-2.0", 16 | "dependencies": { 17 | "@codebam/cf-workers-telegram-bot": "file:../main", 18 | "marked": "^16.0.0" 19 | }, 20 | "devDependencies": { 21 | "@babel/preset-env": "^7.27.2", 22 | "@cloudflare/vitest-pool-workers": "^0.8.47", 23 | "@cloudflare/workers-types": "^4.20250628.0", 24 | "@eslint/js": "^9.30.0", 25 | "@typescript-eslint/eslint-plugin": "^8.35.0", 26 | "@typescript-eslint/parser": "^8.35.0", 27 | "esbuild": "^0.25.5", 28 | "esbuild-plugin-babel": "^0.2.3", 29 | "esbuild-plugin-glob": "^2.2.3", 30 | "eslint": "^9.30.0", 31 | "eslint-config-prettier": "^10.1.5", 32 | "globals": "^16.2.0", 33 | "prettier": "^3.6.2", 34 | "typescript": "^5.8.3", 35 | "typescript-eslint": "^8.35.0", 36 | "vitest": "3.2.4" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/build_pr.yml: -------------------------------------------------------------------------------- 1 | name: build pull request 2 | on: pull_request 3 | 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 5 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: latest 17 | registry-url: https://registry.npmjs.org/ 18 | - id: node-modules-cache 19 | uses: actions/cache@v3 20 | with: 21 | path: | 22 | node_modules 23 | key: node-modules-${{ hashFiles('package-lock.json') }} 24 | restore-keys: node-modules- 25 | - run: npm i 26 | - run: npm ci 27 | - run: npm run lint --workspace=packages 28 | - run: npm run test --workspace=packages 29 | - run: npm run build --workspace=packages 30 | merge-dependabot: 31 | needs: build 32 | runs-on: ubuntu-latest 33 | if: ${{ github.actor == 'dependabot[bot]' }} 34 | steps: 35 | - name: Dependabot metadata 36 | id: metadata 37 | uses: dependabot/fetch-metadata@v1 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | - name: Merge PR 41 | run: gh pr merge --auto --merge "$PR_URL" 42 | env: 43 | PR_URL: ${{github.event.pull_request.html_url}} 44 | GH_TOKEN: ${{ github.token }} 45 | -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # ESLint is a tool for identifying and reporting on patterns 6 | # found in ECMAScript/JavaScript code. 7 | # More details at https://github.com/eslint/eslint 8 | # and https://eslint.org 9 | 10 | name: ESLint 11 | 12 | on: 13 | push: 14 | branches: [ "master" ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ "master" ] 18 | schedule: 19 | - cron: '18 17 * * 4' 20 | 21 | jobs: 22 | eslint: 23 | name: Run eslint scanning 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v4 32 | 33 | - name: Install ESLint 34 | run: | 35 | npm install eslint 36 | npm install @microsoft/eslint-formatter-sarif@3.1.0 37 | 38 | - name: Run ESLint 39 | env: 40 | SARIF_ESLINT_IGNORE_SUPPRESSED: "true" 41 | run: npx eslint . 42 | --format @microsoft/eslint-formatter-sarif 43 | --output-file eslint-results.sarif 44 | continue-on-error: true 45 | 46 | - name: Upload analysis results to GitHub 47 | uses: github/codeql-action/upload-sarif@v3 48 | with: 49 | sarif_file: eslint-results.sarif 50 | wait-for-processing: true 51 | 52 | -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #795E26; 3 | --dark-hl-0: #DCDCAA; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #A31515; 7 | --dark-hl-2: #CE9178; 8 | --light-hl-3: #800000; 9 | --dark-hl-3: #569CD6; 10 | --light-hl-4: #0000FF; 11 | --dark-hl-4: #CE9178; 12 | --light-code-background: #FFFFFF; 13 | --dark-code-background: #1E1E1E; 14 | } 15 | 16 | @media (prefers-color-scheme: light) { :root { 17 | --hl-0: var(--light-hl-0); 18 | --hl-1: var(--light-hl-1); 19 | --hl-2: var(--light-hl-2); 20 | --hl-3: var(--light-hl-3); 21 | --hl-4: var(--light-hl-4); 22 | --code-background: var(--light-code-background); 23 | } } 24 | 25 | @media (prefers-color-scheme: dark) { :root { 26 | --hl-0: var(--dark-hl-0); 27 | --hl-1: var(--dark-hl-1); 28 | --hl-2: var(--dark-hl-2); 29 | --hl-3: var(--dark-hl-3); 30 | --hl-4: var(--dark-hl-4); 31 | --code-background: var(--dark-code-background); 32 | } } 33 | 34 | :root[data-theme='light'] { 35 | --hl-0: var(--light-hl-0); 36 | --hl-1: var(--light-hl-1); 37 | --hl-2: var(--light-hl-2); 38 | --hl-3: var(--light-hl-3); 39 | --hl-4: var(--light-hl-4); 40 | --code-background: var(--light-code-background); 41 | } 42 | 43 | :root[data-theme='dark'] { 44 | --hl-0: var(--dark-hl-0); 45 | --hl-1: var(--dark-hl-1); 46 | --hl-2: var(--dark-hl-2); 47 | --hl-3: var(--dark-hl-3); 48 | --hl-4: var(--dark-hl-4); 49 | --code-background: var(--dark-code-background); 50 | } 51 | 52 | .hl-0 { color: var(--hl-0); } 53 | .hl-1 { color: var(--hl-1); } 54 | .hl-2 { color: var(--hl-2); } 55 | .hl-3 { color: var(--hl-3); } 56 | .hl-4 { color: var(--hl-4); } 57 | pre, code { background: var(--code-background); } 58 | -------------------------------------------------------------------------------- /packages/main/src/main.ts: -------------------------------------------------------------------------------- 1 | import TelegramBot from './telegram_bot.js'; 2 | import TelegramExecutionContext from './telegram_execution_context.js'; 3 | import Webhook from './webhook.js'; 4 | import TelegramApi from './telegram_api.js'; 5 | import TelegramCommand from './types/TelegramCommand.js'; 6 | import TelegramFrom from './types/TelegramFrom.js'; 7 | import TelegramChat from './types/TelegramChat.js'; 8 | import TelegramUser from './types/TelegramUser.js'; 9 | import TelegramMessageEntity from './types/TelegramMessageEntity.js'; 10 | import TelegramPhotoSize from './types/TelegramPhotoSize.js'; 11 | import TelegramMessage from './types/TelegramMessage.js'; 12 | import TelegramInputMessageContent from './types/TelegramInputMessageContent.js'; 13 | import TelegramInlineQuery from './types/TelegramInlineQuery.js'; 14 | import TelegramUpdate from './types/TelegramUpdate.js'; 15 | import PartialTelegramUpdate from './types/PartialTelegramUpdate.js'; 16 | import TelegramInlineQueryType from './types/TelegramInlineQueryType.js'; 17 | import TelegramInlineQueryResult from './types/TelegramInlineQueryResult.js'; 18 | import TelegramInlineQueryResultPhoto from './types/TelegramInlineQueryResultPhoto.js'; 19 | import TelegramInlineQueryResultArticle from './types/TelegramInlineQueryResultArticle.js'; 20 | import ChatPermissions from './types/ChatPermissions.js'; 21 | 22 | export default TelegramBot; 23 | export { 24 | TelegramBot, 25 | TelegramExecutionContext, 26 | Webhook, 27 | TelegramApi, 28 | TelegramCommand, 29 | TelegramFrom, 30 | TelegramChat, 31 | TelegramUser, 32 | TelegramMessageEntity, 33 | TelegramPhotoSize, 34 | TelegramMessage, 35 | TelegramInputMessageContent, 36 | TelegramInlineQuery, 37 | TelegramUpdate, 38 | PartialTelegramUpdate, 39 | TelegramInlineQueryType, 40 | TelegramInlineQueryResult, 41 | TelegramInlineQueryResultPhoto, 42 | TelegramInlineQueryResultArticle, 43 | ChatPermissions, 44 | }; 45 | -------------------------------------------------------------------------------- /packages/main/src/types.ts: -------------------------------------------------------------------------------- 1 | import TelegramBot from './telegram_bot.js'; 2 | import TelegramExecutionContext from './telegram_execution_context.js'; 3 | import Webhook from './webhook.js'; 4 | import TelegramApi from './telegram_api.js'; 5 | import TelegramCommand from './types/TelegramCommand.js'; 6 | import TelegramFrom from './types/TelegramFrom.js'; 7 | import TelegramChat from './types/TelegramChat.js'; 8 | import TelegramUser from './types/TelegramUser.js'; 9 | import TelegramMessageEntity from './types/TelegramMessageEntity.js'; 10 | import TelegramPhotoSize from './types/TelegramPhotoSize.js'; 11 | import TelegramMessage from './types/TelegramMessage.js'; 12 | import TelegramInputMessageContent from './types/TelegramInputMessageContent.js'; 13 | import TelegramInlineQuery from './types/TelegramInlineQuery.js'; 14 | import TelegramUpdate from './types/TelegramUpdate.js'; 15 | import PartialTelegramUpdate from './types/PartialTelegramUpdate.js'; 16 | import TelegramInlineQueryType from './types/TelegramInlineQueryType.js'; 17 | import TelegramInlineQueryResult from './types/TelegramInlineQueryResult.js'; 18 | import TelegramInlineQueryResultPhoto from './types/TelegramInlineQueryResultPhoto.js'; 19 | import TelegramInlineQueryResultArticle from './types/TelegramInlineQueryResultArticle.js'; 20 | import ChatPermissions from './types/ChatPermissions.js'; 21 | 22 | export default TelegramBot; 23 | export { 24 | TelegramBot, 25 | TelegramExecutionContext, 26 | Webhook, 27 | TelegramApi, 28 | TelegramCommand, 29 | TelegramFrom, 30 | TelegramChat, 31 | TelegramUser, 32 | TelegramMessageEntity, 33 | TelegramPhotoSize, 34 | TelegramMessage, 35 | TelegramInputMessageContent, 36 | TelegramInlineQuery, 37 | TelegramUpdate, 38 | PartialTelegramUpdate, 39 | TelegramInlineQueryType, 40 | TelegramInlineQueryResult, 41 | TelegramInlineQueryResultPhoto, 42 | TelegramInlineQueryResultArticle, 43 | ChatPermissions, 44 | }; 45 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramUpdate.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQuery from './TelegramInlineQuery.js'; 2 | import TelegramMessage from './TelegramMessage.js'; 3 | import PartialTelegramUpdate from './PartialTelegramUpdate.js'; 4 | import TelegramCallbackQuery from './TelegramCallbackQuery.js'; 5 | import TelegramBusinessMessage from './TelegramBusinessMessage.js'; 6 | 7 | export default class TelegramUpdate { 8 | update_id: number; 9 | message?: TelegramMessage; 10 | edited_message?: TelegramMessage; 11 | channel_post?: TelegramMessage; 12 | edited_channel_post?: TelegramMessage; 13 | inline_query?: TelegramInlineQuery; 14 | // chosen_inline_result?: TelegramChosenInlineResult; 15 | callback_query?: TelegramCallbackQuery; 16 | business_message?: TelegramBusinessMessage; 17 | // shipping_query?: TelegramShippingQuery; 18 | // pre_checkout_query?: TelegramPreCheckoutQuery; 19 | // poll?: TelegramPoll; 20 | // poll_answer?: TelegramPollAnswer; 21 | // my_chat_member?: TelegramChatMemberUpdated; 22 | // chat_member?: TelegramChatMemberUpdated; 23 | // chat_join_request: TelegramChatJoinRequest; 24 | constructor(update: PartialTelegramUpdate) { 25 | this.update_id = update.update_id ?? 0; 26 | this.message = update.message; 27 | this.edited_message = update.edited_message; 28 | this.channel_post = update.channel_post; 29 | this.edited_channel_post = update.edited_channel_post; 30 | this.inline_query = update.inline_query; 31 | this.business_message = update.business_message; 32 | // chosen_inline_result = update.chosen_inline_result; 33 | // callback_query = update.callback_query; 34 | // shipping_query = update.shipping_query; 35 | // pre_checkout_query = update.pre_checkout_query; 36 | // poll = update.poll; 37 | // poll_answer = update.poll_answer; 38 | // my_chat_member = update.my_chat_member; 39 | // chat_member = update.chat_member; 40 | // chat_join_request = update.chat_join_request; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramMessage.ts: -------------------------------------------------------------------------------- 1 | import TelegramChat from './TelegramChat.js'; 2 | import { TelegramDocument } from './TelegramDocument.js'; 3 | import TelegramFrom from './TelegramFrom.js'; 4 | import TelegramMessageEntity from './TelegramMessageEntity.js'; 5 | import TelegramPhotoSize from './TelegramPhotoSize.js'; 6 | import TelegramUser from './TelegramUser.js'; 7 | 8 | interface TelegramMessage { 9 | message_id: number; 10 | from: TelegramFrom; 11 | sender_chat?: TelegramChat; 12 | date: number; 13 | chat: TelegramChat; 14 | forward_from?: TelegramUser; 15 | forward_from_chat?: TelegramChat; 16 | forward_from_message_id?: number; 17 | forward_signature?: string; 18 | forward_sender_name?: string; 19 | forward_date?: number; 20 | is_automatic_forward?: boolean; 21 | reply_to_message?: TelegramMessage; 22 | via_bot?: TelegramUser; 23 | edit_date?: number; 24 | has_protected_content?: boolean; 25 | media_group_id?: string; 26 | author_signature?: string; 27 | text?: string; 28 | entities?: TelegramMessageEntity[]; 29 | // animation?: TelegramAnimation; 30 | // audio?: TelegramAudio; 31 | document?: TelegramDocument; 32 | photo?: TelegramPhotoSize[]; 33 | // sticker?: TelegramSticker; 34 | // video?: TelegramVideo; 35 | // video_note?: TelegramVideoNote; 36 | // voice?: TelegramVoice; 37 | caption?: string; 38 | caption_entities?: TelegramMessageEntity[]; 39 | // contact?: TelegramContact; 40 | // dice?: TelegramDice; 41 | // poll?: TelegramPoll; 42 | // venue?: TelegramVenue; 43 | // location?: TelegramLocation; 44 | new_chat_members?: TelegramUser[]; 45 | new_chat_member?: TelegramUser; 46 | left_chat_member?: TelegramUser; 47 | new_chat_title?: string; 48 | // new_chat_photo?: TelegramPhotoSize[]; 49 | delete_chat_photo?: boolean; 50 | group_chat_created?: boolean; 51 | supergroup_chat_created?: boolean; 52 | channel_chat_created?: boolean; 53 | // message_auto_delete_timer_changed?: TelegramAutoDeleteTimerChanged; 54 | migrate_to_chat_id?: number; 55 | migrate_from_chat_id?: number; 56 | pinned_message?: TelegramMessage; 57 | // invoice?: TelegramInvoice; 58 | // successful_payment?: TelegramSuccessfulPayment; 59 | connected_website?: string; 60 | } 61 | export default TelegramMessage; 62 | -------------------------------------------------------------------------------- /packages/main/src/types/TelegramBusinessMessage.ts: -------------------------------------------------------------------------------- 1 | import TelegramChat from './TelegramChat.js'; 2 | import { TelegramDocument } from './TelegramDocument.js'; 3 | import TelegramFrom from './TelegramFrom.js'; 4 | import TelegramMessageEntity from './TelegramMessageEntity.js'; 5 | import TelegramPhotoSize from './TelegramPhotoSize.js'; 6 | import TelegramUser from './TelegramUser.js'; 7 | import TelegramMessage from './TelegramMessage.js'; 8 | 9 | interface TelegramBusinessMessage { 10 | message_id: number; 11 | business_connection_id: string; 12 | from: TelegramFrom; 13 | sender_chat?: TelegramChat; 14 | date: number; 15 | chat: TelegramChat; 16 | forward_from?: TelegramUser; 17 | forward_from_chat?: TelegramChat; 18 | forward_from_message_id?: number; 19 | forward_signature?: string; 20 | forward_sender_name?: string; 21 | forward_date?: number; 22 | is_automatic_forward?: boolean; 23 | reply_to_message?: TelegramMessage; 24 | via_bot?: TelegramUser; 25 | edit_date?: number; 26 | has_protected_content?: boolean; 27 | media_group_id?: string; 28 | author_signature?: string; 29 | text?: string; 30 | entities?: TelegramMessageEntity[]; 31 | // animation?: TelegramAnimation; 32 | // audio?: TelegramAudio; 33 | document?: TelegramDocument; 34 | photo?: TelegramPhotoSize[]; 35 | // sticker?: TelegramSticker; 36 | // video?: TelegramVideo; 37 | // video_note?: TelegramVideoNote; 38 | // voice?: TelegramVoice; 39 | caption?: string; 40 | caption_entities?: TelegramMessageEntity[]; 41 | // contact?: TelegramContact; 42 | // dice?: TelegramDice; 43 | // poll?: TelegramPoll; 44 | // venue?: TelegramVenue; 45 | // location?: TelegramLocation; 46 | new_chat_members?: TelegramUser[]; 47 | new_chat_member?: TelegramUser; 48 | left_chat_member?: TelegramUser; 49 | new_chat_title?: string; 50 | // new_chat_photo?: TelegramPhotoSize[]; 51 | delete_chat_photo?: boolean; 52 | group_chat_created?: boolean; 53 | supergroup_chat_created?: boolean; 54 | channel_chat_created?: boolean; 55 | // message_auto_delete_timer_changed?: TelegramAutoDeleteTimerChanged; 56 | migrate_to_chat_id?: number; 57 | migrate_from_chat_id?: number; 58 | pinned_message?: TelegramMessage; 59 | // invoice?: TelegramInvoice; 60 | // successful_payment?: TelegramSuccessfulPayment; 61 | connected_website?: string; 62 | } 63 | export default TelegramBusinessMessage; 64 | -------------------------------------------------------------------------------- /packages/main/src/webhook.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webhook class for managing Telegram bot webhook configuration. 3 | * Handles setting up and configuring webhooks for Telegram bots. 4 | */ 5 | export default class Webhook { 6 | /** Base URL for the Telegram Bot API */ 7 | private readonly api: URL; 8 | 9 | /** Webhook URL that Telegram will send updates to */ 10 | private readonly webhook: URL; 11 | 12 | /** 13 | * Creates a new Webhook instance. 14 | * 15 | * @param token - The Telegram bot token 16 | * @param request - The incoming request object used to determine the webhook URL 17 | */ 18 | constructor(token: string, request: Request) { 19 | this.api = new URL(`https://api.telegram.org/bot${token}`); 20 | this.webhook = new URL(`${new URL(request.url).origin}/${token}`); 21 | } 22 | 23 | /** 24 | * Sets the webhook URL for the Telegram bot. 25 | * 26 | * @returns Promise that resolves to the fetch response from Telegram 27 | * @throws Will throw an error if the fetch request fails 28 | */ 29 | async set(): Promise { 30 | const baseUrl = this.api.toString(); 31 | const url = new URL(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}setWebhook`); 32 | 33 | // Configure webhook parameters 34 | const params = new URLSearchParams({ 35 | url: this.webhook.toString(), 36 | max_connections: '100', 37 | allowed_updates: JSON.stringify(['message', 'inline_query', 'business_message', 'business_connection']), 38 | drop_pending_updates: 'true', 39 | }); 40 | 41 | try { 42 | return await fetch(`${url.toString()}?${params.toString()}`); 43 | } catch (error) { 44 | console.error('Failed to set webhook:', error); 45 | throw error; 46 | } 47 | } 48 | 49 | /** 50 | * Removes the webhook configuration from Telegram. 51 | * 52 | * @returns Promise that resolves to the fetch response from Telegram 53 | * @throws Will throw an error if the fetch request fails 54 | */ 55 | async delete(): Promise { 56 | const baseUrl = this.api.toString(); 57 | const url = new URL(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}deleteWebhook`); 58 | 59 | try { 60 | return await fetch(url.toString()); 61 | } catch (error) { 62 | console.error('Failed to delete webhook:', error); 63 | throw error; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | CF Workers Telegram Bot 5 |
6 |

7 | 8 |
9 | Docs 10 | Wiki 11 |
12 | 13 |

14 | GitHub stars 15 | 16 | GitHub issues 17 | GitHub forks 18 | npm version 19 |

20 | 21 | ![screenshot of cf-workers-telegram-bot](https://raw.githubusercontent.com/codebam/cf-workers-telegram-bot/master/assets/screenshot.png) 22 | 23 | ```sh 24 | npm i @codebam/cf-workers-telegram-bot 25 | ``` 26 | 27 | See [cwtb-consumer](https://github.com/codebam/cwtb-consumer) for an example of what a bot might look like. Just import from `@codebam/cf-workers-telegram-bot`. 28 | 29 | See [my blog post](https://seanbehan.ca/posts/cf-workers-telegram-bot) for a more in-depth guide for how to set up a bot. 30 | 31 | - `npm create cloudflare@latest` 32 | - `npx wrangler login` 33 | - `npx wrangler secret put SECRET_TELEGRAM_API_TOKEN`, set it to your telegram bot token that you got from `@BotFather` 34 | - `npx wrangler deploy` 35 | - Open this url in your browser to set your webhook `https://your-worker.username.workers.dev/SECRET_TELEGRAM_API_TOKEN?command=set` 36 | 37 | To set up GitHub actions to deploy when you push, see https://github.com/cloudflare/wrangler-action 38 | 39 | --- 40 | 41 | These instructions are for if you want to deploy a copy of the bot along with 42 | the library. Such as if you need extra API requests that haven't been 43 | implemented yet. 44 | 45 | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/codebam/cf-workers-telegram-bot) 46 | 47 | - Click the deploy button 48 | - Navigate to your new **GitHub repository > Settings > Secrets** and add the following secrets: 49 | 50 | ```yaml 51 | - Name: CLOUDFLARE_API_TOKEN (should be added automatically) 52 | - Name: CLOUDFLARE_ACCOUNT_ID (should be added automatically) 53 | 54 | - Name: SECRET_TELEGRAM_API_TOKEN 55 | - Value: your-telegram-bot-token 56 | ``` 57 | 58 | - Push to `master` to trigger a deploy 59 | -------------------------------------------------------------------------------- /packages/main/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | CF Workers Telegram Bot 5 |
6 |

7 | 8 |
9 | Docs 10 | Wiki 11 |
12 | 13 |

14 | GitHub stars 15 | 16 | GitHub issues 17 | GitHub forks 18 | npm version 19 |

20 | 21 | ![screenshot of cf-workers-telegram-bot](https://raw.githubusercontent.com/codebam/cf-workers-telegram-bot/master/assets/screenshot.png) 22 | 23 | ```sh 24 | npm i @codebam/cf-workers-telegram-bot 25 | ``` 26 | 27 | See [cwtb-consumer](https://github.com/codebam/cwtb-consumer) for an example of what a bot might look like. Just import from `@codebam/cf-workers-telegram-bot`. 28 | 29 | See [my blog post](https://seanbehan.ca/posts/cf-workers-telegram-bot) for a more in-depth guide for how to set up a bot. 30 | 31 | - `npm create cloudflare@latest` 32 | - `npx wrangler login` 33 | - `npx wrangler secret put SECRET_TELEGRAM_API_TOKEN`, set it to your telegram bot token that you got from `@BotFather` 34 | - `npx wrangler deploy` 35 | - Open this url in your browser to set your webhook `https://your-worker.username.workers.dev/SECRET_TELEGRAM_API_TOKEN?command=set` 36 | 37 | To set up GitHub actions to deploy when you push, see https://github.com/cloudflare/wrangler-action 38 | 39 | --- 40 | 41 | These instructions are for if you want to deploy a copy of the bot along with 42 | the library. Such as if you need extra API requests that haven't been 43 | implemented yet. 44 | 45 | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/codebam/cf-workers-telegram-bot) 46 | 47 | - Click the deploy button 48 | - Navigate to your new **GitHub repository > Settings > Secrets** and add the following secrets: 49 | 50 | ```yaml 51 | - Name: CLOUDFLARE_API_TOKEN (should be added automatically) 52 | - Name: CLOUDFLARE_ACCOUNT_ID (should be added automatically) 53 | 54 | - Name: SECRET_TELEGRAM_API_TOKEN 55 | - Value: your-telegram-bot-token 56 | ``` 57 | 58 | - Push to `master` to trigger a deploy 59 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '23 4 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Use only 'java' to analyze code written in Java, Kotlin or both 38 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 39 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v3 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v2 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | 54 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 55 | # queries: security-extended,security-and-quality 56 | 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v2 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 65 | 66 | # If the Autobuild fails above, remove it and uncomment the following three lines. 67 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 68 | 69 | # - run: | 70 | # echo "Run, Build Application using script" 71 | # ./location_of_script_within_repo/buildscript.sh 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v2 75 | with: 76 | category: "/language:${{matrix.language}}" 77 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # Or your preferred channel 4 | }; 5 | 6 | outputs = 7 | inputs@{ self, nixpkgs, ... }: 8 | let 9 | 10 | forAllSystems = 11 | function: 12 | nixpkgs.lib.genAttrs 13 | [ 14 | "x86_64-linux" 15 | "aarch64-linux" 16 | "x86_64-darwin" 17 | "aarch64-darwin" 18 | ] 19 | ( 20 | system: 21 | function ( 22 | import nixpkgs { 23 | inherit system; 24 | # overlays = [ ... ]; # You can add overlays here if needed 25 | # config = { ... }; # Or pkgs configuration 26 | } 27 | ) 28 | ); 29 | 30 | in 31 | rec { 32 | devShell = forAllSystems ( 33 | pkgs: 34 | pkgs.mkShell { 35 | name = "my-dev-environment"; 36 | buildInputs = with pkgs; [ 37 | nodejs_latest 38 | nodePackages_latest.typescript-language-server 39 | nodePackages_latest.prettier 40 | vscode-langservers-extracted 41 | ]; 42 | # You can add shell hooks or environment variables here too 43 | # shellHook = '' 44 | # echo "Welcome to the development shell!" 45 | # export MY_VAR="hello" 46 | # ''; 47 | } 48 | ); 49 | 50 | devShells = devShell; 51 | 52 | packages = forAllSystems (pkgs: { 53 | dockerImage = pkgs.dockerTools.buildImage { 54 | name = "cf-workers-telegram-bot"; 55 | tag = "latest"; 56 | fromImage = pkgs.dockerTools.pullImage { 57 | imageName = "jacoblincool/workerd"; 58 | imageDigest = "sha256:9ef73cacee85040a3c742b22ca0b8ee527d20465af58d4408f024cec1caf347c"; 59 | sha256 = "sha256-7Ee5kG8qvGIzk3uyE/35fojl/qbsBvuBquJBWZGIqvA="; 60 | }; 61 | copyToRoot = pkgs.buildEnv { 62 | name = "image-root"; 63 | paths = [ 64 | (pkgs.stdenv.mkDerivation { 65 | name = "docker-root"; 66 | buildCommand = '' 67 | mkdir -p $out/worker 68 | cp ${./worker.capnp} $out/worker/worker.capnp 69 | cp -r ${self.packages.${pkgs.system}.default}/* $out/ 70 | ''; 71 | }) 72 | ]; 73 | }; 74 | config = { 75 | Cmd = [ 76 | "/workerd" 77 | "serve" 78 | "/worker/worker.capnp" 79 | ]; 80 | WorkingDir = "/"; 81 | }; 82 | }; 83 | default = pkgs.buildNpmPackage { 84 | name = "cf-workers-telegram-bot"; 85 | src = ./.; 86 | npmDepsHash = "sha256-tlxWjtGMnTTMS4or/hZuWEoe+DI6eZRHbOKLsXx/LIY="; 87 | installPhase = '' 88 | runHook preInstall 89 | mkdir -p $out/worker 90 | cp -r dist/* $out/worker 91 | runHook postInstall 92 | ''; 93 | }; 94 | }); 95 | 96 | apps = forAllSystems (pkgs: { 97 | default = { 98 | type = "app"; 99 | program = "${pkgs.writeScriptBin "run-worker" '' 100 | #!/bin/sh 101 | workerd serve ${self.packages.${pkgs.system}.default}/worker.mjs "$@" 102 | ''}/bin/run-worker"; 103 | }; 104 | }); 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /packages/main/src/telegram_bot.ts: -------------------------------------------------------------------------------- 1 | import TelegramUpdate from './types/TelegramUpdate.js'; 2 | import TelegramExecutionContext from './telegram_execution_context.js'; 3 | import Webhook from './webhook.js'; 4 | 5 | /** Class representing a telegram bot. */ 6 | export default class TelegramBot { 7 | /** The telegram token */ 8 | token: string; 9 | /** The telegram api URL */ 10 | api: URL; 11 | /** The telegram webhook object */ 12 | webhook: Webhook = new Webhook('', new Request('http://127.0.0.1')); 13 | /** The telegram update object */ 14 | update: TelegramUpdate = new TelegramUpdate({}); 15 | /** The telegram commands record map */ 16 | commands: Record Promise> = {}; 17 | /** The current bot context */ 18 | currentContext!: TelegramExecutionContext; 19 | /** Default command to use when no matching command is found */ 20 | defaultCommand = ':message'; 21 | 22 | /** 23 | * Create a bot 24 | * @param token - the telegram secret token 25 | * @param options - optional configuration for the bot 26 | */ 27 | constructor(token: string, options?: { defaultCommand?: string }) { 28 | this.token = token; 29 | this.api = new URL('https://api.telegram.org/bot' + token); 30 | 31 | if (options?.defaultCommand) { 32 | this.defaultCommand = options.defaultCommand; 33 | } 34 | 35 | // Register default handler for the default command to avoid errors 36 | this.commands[this.defaultCommand] = () => Promise.resolve(new Response('Command not implemented')); 37 | } 38 | 39 | /** 40 | * Register a function on the bot 41 | * @param event - the event or command name 42 | * @param callback - the bot context 43 | */ 44 | on(event: string, callback: (ctx: TelegramExecutionContext) => Promise) { 45 | this.commands[event] = callback; 46 | return this; 47 | } 48 | 49 | /** 50 | * Register multiple command handlers at once 51 | * @param handlers - object mapping command names to handler functions 52 | */ 53 | registerHandlers(handlers: Record Promise>) { 54 | for (const [event, callback] of Object.entries(handlers)) { 55 | this.on(event, callback); 56 | } 57 | return this; 58 | } 59 | 60 | /** 61 | * Determine the command from the update 62 | * @param ctx - the execution context 63 | * @param args - command arguments 64 | * @returns the command string 65 | */ 66 | private determineCommand(ctx: TelegramExecutionContext, args: string[]): string { 67 | // First check if it's a special update type 68 | switch (ctx.update_type) { 69 | case 'photo': 70 | return ':photo' in this.commands ? ':photo' : this.defaultCommand; 71 | case 'document': 72 | return ':document' in this.commands ? ':document' : this.defaultCommand; 73 | case 'callback': 74 | return ':callback' in this.commands ? ':callback' : this.defaultCommand; 75 | case 'inline': 76 | return ':inline' in this.commands ? ':inline' : this.defaultCommand; 77 | } 78 | 79 | // Then check if it's a command starting with / 80 | if (args.at(0)?.startsWith('/')) { 81 | const command = args.at(0)?.slice(1) ?? ''; 82 | return command in this.commands ? command : this.defaultCommand; 83 | } 84 | 85 | return this.defaultCommand; 86 | } 87 | 88 | /** 89 | * Parse arguments from the update 90 | * @param ctx - the execution context 91 | * @returns array of argument strings 92 | */ 93 | private parseArguments(ctx: TelegramExecutionContext): string[] { 94 | switch (ctx.update_type) { 95 | case 'message': 96 | case 'business_message': 97 | return this.update.message?.text?.split(' ') ?? []; 98 | case 'inline': 99 | return this.update.inline_query?.query.split(' ') ?? []; 100 | default: 101 | return []; 102 | } 103 | } 104 | 105 | /** 106 | * Handle a request on a given bot 107 | * @param request - the request to handle 108 | */ 109 | async handle(request: Request): Promise { 110 | this.webhook = new Webhook(this.token, request); 111 | const url = new URL(request.url); 112 | 113 | // Check if the request is for this bot 114 | if (`/${this.token}` !== url.pathname) { 115 | return new Response('Invalid token', { status: 404 }); 116 | } 117 | 118 | // Handle different HTTP methods 119 | switch (request.method) { 120 | case 'POST': { 121 | try { 122 | this.update = await request.json(); 123 | console.log(this.update); 124 | 125 | const ctx = new TelegramExecutionContext(this, this.update); 126 | this.currentContext = ctx; 127 | 128 | const args = this.parseArguments(ctx); 129 | const command = this.determineCommand(ctx, args); 130 | 131 | return await this.commands[command](ctx); 132 | } catch (error) { 133 | console.error('Error handling Telegram update:', error); 134 | return new Response('Error processing request', { status: 500 }); 135 | } 136 | } 137 | 138 | case 'GET': { 139 | const command = url.searchParams.get('command'); 140 | if (command === 'set') { 141 | return this.webhook.set(); 142 | } 143 | return new Response('Invalid command', { status: 400 }); 144 | } 145 | 146 | default: 147 | return new Response('Method not allowed', { status: 405 }); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /docs/hierarchy.html: -------------------------------------------------------------------------------- 1 | @codebam/cf-workers-telegram-bot
-------------------------------------------------------------------------------- /docs/types/TelegramInlineQueryType.html: -------------------------------------------------------------------------------- 1 | TelegramInlineQueryType | @codebam/cf-workers-telegram-bot
TelegramInlineQueryType: "article" | "photo" | "gif" | "mpeg4_gif" | "video" | "audio" | "voice" | "document" | "location" | "venue" | "contact" | "game" | "sticker"
-------------------------------------------------------------------------------- /docs/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = "data:application/octet-stream;base64,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"; -------------------------------------------------------------------------------- /docs/types/TelegramCommand.html: -------------------------------------------------------------------------------- 1 | TelegramCommand | @codebam/cf-workers-telegram-bot
TelegramCommand: ((bot, update, args) => Promise<Response>)

Type declaration

    • (bot, update, args): Promise<Response>
    • Parameters

      Returns Promise<Response>

-------------------------------------------------------------------------------- /packages/main/src/telegram_execution_context.ts: -------------------------------------------------------------------------------- 1 | import TelegramApi from './telegram_api.js'; 2 | import TelegramBot from './telegram_bot.js'; 3 | import TelegramInlineQueryResultArticle from './types/TelegramInlineQueryResultArticle.js'; 4 | import TelegramInlineQueryResultPhoto from './types/TelegramInlineQueryResultPhoto.js'; 5 | import TelegramUpdate from './types/TelegramUpdate.js'; 6 | import TelegramInlineQueryResultVideo from './types/TelegramInlineQueryResultVideo.js'; 7 | 8 | /** Class representing the context of execution */ 9 | export default class TelegramExecutionContext { 10 | /** an instance of the telegram bot */ 11 | bot: TelegramBot; 12 | /** an instance of the telegram update */ 13 | update: TelegramUpdate; 14 | /** string representing the type of update that was sent */ 15 | update_type = ''; 16 | /** reference to TelegramApi class */ 17 | api = new TelegramApi(); 18 | 19 | /** 20 | * Create a telegram execution context 21 | * @param bot - the telegram bot 22 | * @param update - the telegram update 23 | */ 24 | constructor(bot: TelegramBot, update: TelegramUpdate) { 25 | this.bot = bot; 26 | this.update = update; 27 | 28 | this.update_type = this.determineUpdateType(); 29 | } 30 | 31 | /** 32 | * Determine the type of update received 33 | * @returns The update type as a string 34 | */ 35 | private determineUpdateType(): string { 36 | if (this.update.message?.photo) { 37 | return 'photo'; 38 | } else if (this.update.message?.text) { 39 | return 'message'; 40 | } else if (this.update.inline_query?.query) { 41 | return 'inline'; 42 | } else if (this.update.message?.document) { 43 | return 'document'; 44 | } else if (this.update.callback_query?.id) { 45 | return 'callback'; 46 | } else if (this.update.business_message) { 47 | return 'business_message'; 48 | } 49 | return ''; 50 | } 51 | 52 | /** 53 | * Get the chat ID from the current update 54 | * @returns The chat ID as a string or empty string if not available 55 | */ 56 | private getChatId(): string { 57 | if (this.update.message?.chat.id) { 58 | return this.update.message.chat.id.toString(); 59 | } else if (this.update.business_message?.chat.id) { 60 | return this.update.business_message.chat.id.toString(); 61 | } 62 | return ''; 63 | } 64 | 65 | /** 66 | * Get the message ID from the current update 67 | * @returns The message ID as a string or empty string if not available 68 | */ 69 | private getMessageId(): string { 70 | return this.update.message?.message_id.toString() ?? ''; 71 | } 72 | 73 | /** 74 | * Reply to the last message with a video 75 | * @param video - string to a video on the internet or a file_id on telegram 76 | * @param options - any additional options to pass to sendVideo 77 | * @returns Promise with the API response 78 | */ 79 | async replyVideo(video: string, options: Record = {}) { 80 | switch (this.update_type) { 81 | case 'message': 82 | return await this.api.sendVideo(this.bot.api.toString(), { 83 | ...options, 84 | chat_id: this.getChatId(), 85 | reply_to_message_id: this.getMessageId(), 86 | video, 87 | }); 88 | case 'inline': 89 | return await this.api.answerInline(this.bot.api.toString(), { 90 | ...options, 91 | inline_query_id: this.update.inline_query?.id.toString() ?? '', 92 | results: [new TelegramInlineQueryResultVideo(video)], 93 | }); 94 | 95 | default: 96 | return null; 97 | } 98 | } 99 | 100 | /** 101 | * Get File from telegram file_id 102 | * @param file_id - telegram file_id 103 | * @returns Promise with the file response 104 | */ 105 | async getFile(file_id: string) { 106 | return await this.api.getFile(this.bot.api.toString(), { file_id }, this.bot.token); 107 | } 108 | 109 | /** 110 | * Reply to the last message with a photo 111 | * @param photo - url or file_id to photo 112 | * @param caption - photo caption 113 | * @param options - any additional options to pass to sendPhoto 114 | * @returns Promise with the API response 115 | */ 116 | async replyPhoto(photo: string, caption = '', options: Record = {}) { 117 | switch (this.update_type) { 118 | case 'photo': 119 | case 'message': 120 | return await this.api.sendPhoto(this.bot.api.toString(), { 121 | ...options, 122 | chat_id: this.getChatId(), 123 | reply_to_message_id: this.getMessageId(), 124 | photo, 125 | caption, 126 | }); 127 | case 'inline': 128 | return await this.api.answerInline(this.bot.api.toString(), { 129 | inline_query_id: this.update.inline_query?.id.toString() ?? '', 130 | results: [new TelegramInlineQueryResultPhoto(photo)], 131 | }); 132 | 133 | default: 134 | return null; 135 | } 136 | } 137 | 138 | /** 139 | * Send typing in a chat 140 | * @returns Promise with the API response 141 | */ 142 | async sendTyping() { 143 | switch (this.update_type) { 144 | case 'message': 145 | case 'photo': 146 | case 'document': 147 | return await this.api.sendChatAction(this.bot.api.toString(), { 148 | chat_id: this.getChatId(), 149 | action: 'typing', 150 | }); 151 | case 'business_message': 152 | return await this.api.sendChatAction(this.bot.api.toString(), { 153 | business_connection_id: this.update.business_message?.business_connection_id.toString() ?? '', 154 | chat_id: this.getChatId(), 155 | action: 'typing', 156 | }); 157 | default: 158 | return null; 159 | } 160 | } 161 | 162 | /** 163 | * Reply to an inline message with a title and content 164 | * @param title - title to reply with 165 | * @param message - message contents to reply with 166 | * @param parse_mode - parse mode to use 167 | * @returns Promise with the API response 168 | */ 169 | async replyInline(title: string, message: string, parse_mode = '') { 170 | if (this.update_type === 'inline') { 171 | return await this.api.answerInline(this.bot.api.toString(), { 172 | inline_query_id: this.update.inline_query?.id.toString() ?? '', 173 | results: [new TelegramInlineQueryResultArticle({ content: message, title, parse_mode })], 174 | }); 175 | } 176 | return null; 177 | } 178 | 179 | /** 180 | * Reply to the last message with text 181 | * @param message - text to reply with 182 | * @param parse_mode - one of HTML, MarkdownV2, Markdown, or an empty string for ascii 183 | * @param options - any additional options to pass to sendMessage 184 | * @returns Promise with the API response 185 | */ 186 | async reply(message: string, parse_mode = '', options: Record = {}) { 187 | switch (this.update_type) { 188 | case 'message': 189 | case 'photo': 190 | case 'document': 191 | return await this.api.sendMessage(this.bot.api.toString(), { 192 | ...options, 193 | chat_id: this.getChatId(), 194 | reply_to_message_id: this.getMessageId(), 195 | text: message, 196 | parse_mode, 197 | }); 198 | case 'business_message': 199 | return await this.api.sendMessage(this.bot.api.toString(), { 200 | chat_id: this.getChatId(), 201 | text: message, 202 | business_connection_id: this.update.business_message?.business_connection_id.toString() ?? '', 203 | parse_mode, 204 | }); 205 | case 'callback': 206 | if (this.update.callback_query?.message.chat.id) { 207 | return await this.api.sendMessage(this.bot.api.toString(), { 208 | ...options, 209 | chat_id: this.update.callback_query.message.chat.id.toString(), 210 | text: message, 211 | parse_mode, 212 | }); 213 | } 214 | return null; 215 | case 'inline': 216 | return await this.replyInline('Response', message, parse_mode); 217 | default: 218 | return null; 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /docs/classes/Update.html: -------------------------------------------------------------------------------- 1 | Update | @codebam/cf-workers-telegram-bot

Hierarchy (view full)

Indexable

[key: string]: unknown

Constructors

Constructors

-------------------------------------------------------------------------------- /packages/main/src/telegram_api.ts: -------------------------------------------------------------------------------- 1 | import TelegramInlineQueryResultArticle from './types/TelegramInlineQueryResultArticle.js'; 2 | import TelegramInlineQueryResultPhoto from './types/TelegramInlineQueryResultPhoto.js'; 3 | import TelegramInlineQueryResultVideo from './types/TelegramInlineQueryResultVideo.js'; 4 | 5 | /** Interface for common Telegram API parameters */ 6 | interface TelegramApiBaseParams { 7 | chat_id: number | string; 8 | business_connection_id?: string | number; 9 | } 10 | 11 | /** Interface for message parameters */ 12 | interface SendMessageParams extends TelegramApiBaseParams { 13 | text: string; 14 | parse_mode: string; 15 | reply_to_message_id?: number | string; 16 | disable_web_page_preview?: boolean; 17 | disable_notification?: boolean; 18 | protect_content?: boolean; 19 | reply_markup?: object; 20 | } 21 | 22 | /** Interface for photo parameters */ 23 | interface SendPhotoParams extends TelegramApiBaseParams { 24 | photo: string; 25 | caption?: string; 26 | parse_mode?: string; 27 | reply_to_message_id?: number | string; 28 | disable_notification?: boolean; 29 | protect_content?: boolean; 30 | reply_markup?: object; 31 | } 32 | 33 | /** Interface for video parameters */ 34 | interface SendVideoParams extends TelegramApiBaseParams { 35 | video: string; 36 | caption?: string; 37 | parse_mode?: string; 38 | reply_to_message_id?: number | string; 39 | disable_notification?: boolean; 40 | protect_content?: boolean; 41 | reply_markup?: object; 42 | } 43 | 44 | /** Interface for chat action parameters */ 45 | interface SendChatActionParams extends TelegramApiBaseParams { 46 | action: string; 47 | } 48 | 49 | /** Interface for callback query parameters */ 50 | interface AnswerCallbackParams { 51 | callback_query_id: number | string; 52 | text?: string; 53 | show_alert?: boolean; 54 | url?: string; 55 | cache_time?: number; 56 | } 57 | 58 | /** Interface for inline query parameters */ 59 | interface AnswerInlineParams { 60 | inline_query_id: number | string; 61 | results: TelegramInlineQueryResultArticle[] | TelegramInlineQueryResultPhoto[] | TelegramInlineQueryResultVideo[]; 62 | cache_time?: number; 63 | is_personal?: boolean; 64 | next_offset?: string; 65 | } 66 | 67 | /** Type for all possible API parameters */ 68 | type TelegramApiParams = 69 | | SendMessageParams 70 | | SendPhotoParams 71 | | SendVideoParams 72 | | SendChatActionParams 73 | | AnswerCallbackParams 74 | | AnswerInlineParams 75 | | Record; 76 | 77 | /** Class representing the Telegram API and all its methods */ 78 | export default class TelegramApi { 79 | /** 80 | * Get the API URL for a given bot API and slug 81 | * @param botApi - full URL to the telegram API without slug 82 | * @param slug - slug to append to the API URL 83 | * @param data - data to append to the request 84 | * @returns Request object with the full URL and parameters 85 | */ 86 | getApiUrl(botApi: string, slug: string, data: TelegramApiParams): Request { 87 | const request = new URL(botApi + (slug.startsWith('/') || botApi.endsWith('/') ? '' : '/') + slug); 88 | const params = new URLSearchParams(); 89 | 90 | for (const [key, value] of Object.entries(data)) { 91 | if (value !== undefined) { 92 | params.append(key, typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value)); 93 | } 94 | } 95 | 96 | return new Request(`${request.toString()}?${params.toString()}`); 97 | } 98 | 99 | /** 100 | * Send a chat action to indicate the bot is doing something 101 | * @param botApi - full URL to the telegram API without slug 102 | * @param data - data to append to the request 103 | * @returns Promise with the API response 104 | */ 105 | async sendChatAction(botApi: string, data: SendChatActionParams): Promise { 106 | const url = this.getApiUrl(botApi, 'sendChatAction', data); 107 | return await fetch(url); 108 | } 109 | 110 | /** 111 | * Get a file with a given file_id 112 | * @param botApi - full URL to the telegram API without slug 113 | * @param data - data to append to the request 114 | * @param token - bot token 115 | * @returns Promise with the file response 116 | */ 117 | async getFile(botApi: string, data: { file_id: string } & Record, token: string): Promise { 118 | if (!data.file_id || data.file_id === '') { 119 | return new Response('No file_id provided', { status: 400 }); 120 | } 121 | 122 | try { 123 | const url = this.getApiUrl(botApi, 'getFile', data); 124 | const response = await fetch(url); 125 | 126 | if (!response.ok) { 127 | return new Response(`API error: ${String(response.status)} ${response.statusText}`, { status: response.status }); 128 | } 129 | 130 | const json: { ok: boolean; result?: { file_path: string }; description?: string } = await response.json(); 131 | 132 | if (!json.ok || !json.result?.file_path) { 133 | return new Response(json.description ?? 'Failed to get file path', { status: 400 }); 134 | } 135 | 136 | return await fetch(`https://api.telegram.org/file/bot${token}/${json.result.file_path}`); 137 | } catch (e) { 138 | console.error(`Error in getFile: ${e instanceof Error ? e.message : String(e)}`); 139 | return new Response(`Error retrieving file: ${e instanceof Error ? e.message : String(e)}`, { status: 500 }); 140 | } 141 | } 142 | 143 | /** 144 | * Send a message to a given botApi 145 | * @param botApi - full URL to the telegram API without slug 146 | * @param data - data to append to the request 147 | * @returns Promise with the API response 148 | */ 149 | async sendMessage(botApi: string, data: SendMessageParams): Promise { 150 | const url = this.getApiUrl(botApi, 'sendMessage', data); 151 | return await fetch(url); 152 | } 153 | 154 | /** 155 | * Send a video message to a given botApi 156 | * @param botApi - full URL to the telegram API without slug 157 | * @param data - data to append to the request 158 | * @returns Promise with the API response 159 | */ 160 | async sendVideo(botApi: string, data: SendVideoParams): Promise { 161 | const url = this.getApiUrl(botApi, 'sendVideo', data); 162 | return await fetch(url); 163 | } 164 | 165 | /** 166 | * Send a photo message to a given botApi 167 | * @param botApi - full URL to the telegram API without slug 168 | * @param data - data to append to the request 169 | * @returns Promise with the API response 170 | */ 171 | async sendPhoto(botApi: string, data: SendPhotoParams): Promise { 172 | const url = this.getApiUrl(botApi, 'sendPhoto', data); 173 | return await fetch(url); 174 | } 175 | 176 | /** 177 | * Send an inline response to a given botApi 178 | * @param botApi - full URL to the telegram API without slug 179 | * @param data - data to append to the request 180 | * @returns Promise with the API response 181 | */ 182 | async answerInline(botApi: string, data: AnswerInlineParams): Promise { 183 | const url = this.getApiUrl(botApi, 'answerInlineQuery', { 184 | inline_query_id: data.inline_query_id, 185 | results: data.results, 186 | cache_time: data.cache_time, 187 | is_personal: data.is_personal, 188 | next_offset: data.next_offset, 189 | }); 190 | return await fetch(url); 191 | } 192 | 193 | /** 194 | * Send a callback response to a given botApi 195 | * @param botApi - full URL to the telegram API without slug 196 | * @param data - data to append to the request 197 | * @returns Promise with the API response 198 | */ 199 | async answerCallback(botApi: string, data: AnswerCallbackParams): Promise { 200 | const url = this.getApiUrl(botApi, 'answerCallbackQuery', data); 201 | return await fetch(url); 202 | } 203 | 204 | /** 205 | * Delete a message 206 | * @param botApi - full URL to the telegram API without slug 207 | * @param data - data to append to the request 208 | * @returns Promise with the API response 209 | */ 210 | async deleteMessage(botApi: string, data: { chat_id: number | string; message_id: number }): Promise { 211 | const url = this.getApiUrl(botApi, 'deleteMessage', data); 212 | return await fetch(url); 213 | } 214 | 215 | /** 216 | * Edit a message text 217 | * @param botApi - full URL to the telegram API without slug 218 | * @param data - data to append to the request 219 | * @returns Promise with the API response 220 | */ 221 | async editMessageText( 222 | botApi: string, 223 | data: { 224 | chat_id?: number | string; 225 | message_id?: number; 226 | inline_message_id?: string; 227 | text: string; 228 | parse_mode?: string; 229 | disable_web_page_preview?: boolean; 230 | reply_markup?: object; 231 | }, 232 | ): Promise { 233 | const url = this.getApiUrl(botApi, 'editMessageText', data); 234 | return await fetch(url); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /docs/classes/WebhookCommands.html: -------------------------------------------------------------------------------- 1 | WebhookCommands | @codebam/cf-workers-telegram-bot

Indexable

[key: string]: (() => Promise<Response>)
    • (): Promise<Response>
    • Returns Promise<Response>

Constructors

Constructors

-------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | @codebam/cf-workers-telegram-bot

@codebam/cf-workers-telegram-bot

2 | 3 |
4 | CF Workers Telegram Bot 5 |
6 |

7 | 8 |
9 | Docs 10 | Wiki 11 |
12 | 13 |

14 | GitHub stars 15 | 16 | GitHub issues 17 | GitHub forks 18 | npm version 19 |

20 | 21 |

screenshot of cf-workers-telegram-bot

22 |
npm i @codebam/cf-workers-telegram-bot
23 | 
24 |

See cwtb-consumer for an example of what a bot might look like. Just import from @codebam/cf-workers-telegram-bot.

25 |

See my blog post for a more in-depth guide for how to set up a bot.

26 |
    27 |
  • npm create cloudflare@latest
  • 28 |
  • npx wrangler login
  • 29 |
  • npx wrangler secret put SECRET_TELEGRAM_API_TOKEN, set it to your telegram bot token that you got from @BotFather
  • 30 |
  • npx wrangler deploy
  • 31 |
  • Open this url in your browser to set your webhook https://your-worker.username.workers.dev/SECRET_TELEGRAM_API_TOKEN?command=set
  • 32 |
33 |

To set up GitHub actions to deploy when you push, see https://github.com/cloudflare/wrangler-action

34 |
35 |

These instructions are for if you want to deploy a copy of the bot along with 36 | the library. Such as if you need extra API requests that haven't been 37 | implemented yet.

38 |

Deploy to Cloudflare Workers

39 |
    40 |
  • Click the deploy button

    41 |
  • 42 |
  • Navigate to your new GitHub repository > Settings > Secrets and add the following secrets:

    43 |
    - Name: CLOUDFLARE_API_TOKEN  (should be added automatically)
    - Name: CLOUDFLARE_ACCOUNT_ID (should be added automatically)

    - Name: SECRET_TELEGRAM_API_TOKEN
    - Value: your-telegram-bot-token 44 |
    45 |
  • 46 |
  • Push to master to trigger a deploy

    47 |
  • 48 |
49 |
-------------------------------------------------------------------------------- /docs/interfaces/TelegramInputMessageContent.html: -------------------------------------------------------------------------------- 1 | TelegramInputMessageContent | @codebam/cf-workers-telegram-bot
interface TelegramInputMessageContent {
    message_text: string;
    parse_mode: string;
}

Properties

Properties

message_text: string
parse_mode: string
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /packages/main/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /docs/classes/TelegramInlineQueryResult.html: -------------------------------------------------------------------------------- 1 | TelegramInlineQueryResult | @codebam/cf-workers-telegram-bot

Hierarchy (view full)

Constructors

Properties

id 3 | type 4 |

Constructors

Properties

id: string
-------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | @codebam/cf-workers-telegram-bot
-------------------------------------------------------------------------------- /docs/classes/Webhook.html: -------------------------------------------------------------------------------- 1 | Webhook | @codebam/cf-workers-telegram-bot

Constructors

Properties

api 3 | webhook 4 |

Methods

set 5 |

Constructors

  • Parameters

    • token: string
    • request: Request<unknown, CfProperties<unknown>>

    Returns Webhook

Properties

api: URL
webhook: URL

Methods

  • Returns Promise<Response>

-------------------------------------------------------------------------------- /docs/interfaces/TelegramFrom.html: -------------------------------------------------------------------------------- 1 | TelegramFrom | @codebam/cf-workers-telegram-bot
interface TelegramFrom {
    first_name: string;
    id: number;
    is_bot: boolean;
    language_code: string;
    username: string;
}

Properties

Properties

first_name: string
id: number
is_bot: boolean
language_code: string
username: string
-------------------------------------------------------------------------------- /docs/interfaces/TelegramPhotoSize.html: -------------------------------------------------------------------------------- 1 | TelegramPhotoSize | @codebam/cf-workers-telegram-bot
interface TelegramPhotoSize {
    file_id: string;
    file_size?: number;
    file_unique_id: string;
    height: number;
    width: number;
}

Properties

Properties

file_id: string
file_size?: number
file_unique_id: string
height: number
width: number
--------------------------------------------------------------------------------