Skip to content

Commit 4a6b459

Browse files
committed
update telegram_bot with augment
1 parent fffab62 commit 4a6b459

File tree

1 file changed

+143
-103
lines changed

1 file changed

+143
-103
lines changed

packages/main/src/telegram_bot.ts

Lines changed: 143 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -4,107 +4,147 @@ import Webhook from './webhook.js';
44

55
/** Class representing a telegram bot. */
66
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<string, (ctx: TelegramExecutionContext) => Promise<Response>> = {};
17-
/** The current bot context */
18-
currentContext!: TelegramExecutionContext;
19-
20-
/**
21-
* Create a bot
22-
* @param token - the telegram secret token
23-
*/
24-
constructor(token: string) {
25-
this.token = token;
26-
this.api = new URL('https://api.telegram.org/bot' + token);
27-
}
28-
29-
/**
30-
* Register a function on the bot
31-
* @param event - the event or command name
32-
* @param callback - the bot context
33-
*/
34-
on(event: string, callback: (ctx: TelegramExecutionContext) => Promise<Response>) {
35-
if (!['on', 'handle'].includes(event)) {
36-
this.commands[event] = callback;
37-
}
38-
return this;
39-
}
40-
41-
/**
42-
* Handle a request on a given bot
43-
* @param request - the request to handle
44-
*/
45-
async handle(request: Request): Promise<Response> {
46-
this.webhook = new Webhook(this.token, request);
47-
const url = new URL(request.url);
48-
if (`/${this.token}` === url.pathname) {
49-
switch (request.method) {
50-
case 'POST': {
51-
this.update = await request.json();
52-
console.log(this.update);
53-
let command = ':message';
54-
let args: string[] = [];
55-
const ctx = new TelegramExecutionContext(this, this.update);
56-
this.currentContext = ctx;
57-
switch (ctx.update_type) {
58-
case 'message': {
59-
args = this.update.message?.text?.split(' ') ?? [];
60-
break;
61-
}
62-
case 'business_message': {
63-
args = this.update.message?.text?.split(' ') ?? [];
64-
break;
65-
}
66-
case 'inline': {
67-
args = this.update.inline_query?.query.split(' ') ?? [];
68-
break;
69-
}
70-
case 'photo': {
71-
command = ':photo';
72-
break;
73-
}
74-
case 'document': {
75-
command = ':document';
76-
break;
77-
}
78-
case 'callback': {
79-
command = ':callback';
80-
break;
81-
}
82-
default:
83-
break;
84-
}
85-
if (args.at(0)?.startsWith('/')) {
86-
command = args.at(0)?.slice(1) ?? ':message';
87-
}
88-
if (!(command in this.commands)) {
89-
command = ':message';
90-
}
91-
return await this.commands[command](ctx);
92-
}
93-
case 'GET': {
94-
switch (url.searchParams.get('command')) {
95-
case 'set':
96-
return this.webhook.set();
97-
98-
default:
99-
break;
100-
}
101-
break;
102-
}
103-
104-
default:
105-
break;
106-
}
107-
}
108-
return new Response('ok');
109-
}
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<string, (ctx: TelegramExecutionContext) => Promise<Response>> = {};
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<Response>) {
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<string, (ctx: TelegramExecutionContext) => Promise<Response>>) {
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<Response> {
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+
}
110150
}

0 commit comments

Comments
 (0)