telemeister 0.1.21 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -13
- package/bin/telemeister-cli.js +1220 -0
- package/bin/telemeister.js +2 -1
- package/dist/bot/index.d.ts +10 -0
- package/dist/bot/index.d.ts.map +1 -0
- package/dist/bot/index.js +9 -0
- package/dist/bot/index.js.map +1 -0
- package/dist/bot/polling.d.ts +20 -0
- package/dist/bot/polling.d.ts.map +1 -0
- package/dist/bot/polling.js +129 -0
- package/dist/bot/polling.js.map +1 -0
- package/dist/bot/session.d.ts +54 -0
- package/dist/bot/session.d.ts.map +1 -0
- package/dist/bot/session.js +89 -0
- package/dist/bot/session.js.map +1 -0
- package/dist/bot/webhook.d.ts +36 -0
- package/dist/bot/webhook.d.ts.map +1 -0
- package/dist/bot/webhook.js +213 -0
- package/dist/bot/webhook.js.map +1 -0
- package/dist/bot-state-types.d.ts.map +1 -0
- package/dist/bot-state-types.js +3 -0
- package/dist/bot-state-types.js.map +1 -0
- package/dist/cli/create-bot.d.ts.map +1 -1
- package/dist/cli/create-bot.js +32 -10
- package/dist/cli/create-bot.js.map +1 -1
- package/dist/cli/state-manager.d.ts.map +1 -1
- package/dist/cli/state-manager.js +14 -1
- package/dist/cli/state-manager.js.map +1 -1
- package/dist/core/app-states.d.ts +8 -0
- package/dist/core/app-states.d.ts.map +1 -0
- package/dist/core/app-states.js +8 -0
- package/dist/core/app-states.js.map +1 -0
- package/dist/core/bot/polling.d.ts.map +1 -1
- package/dist/core/bot/polling.js +4 -7
- package/dist/core/bot/polling.js.map +1 -1
- package/dist/core/bot/webhook.d.ts.map +1 -1
- package/dist/core/bot/webhook.js +28 -9
- package/dist/core/bot/webhook.js.map +1 -1
- package/dist/core/builder.d.ts +1 -1
- package/dist/core/builder.d.ts.map +1 -1
- package/dist/core/builder.js +2 -2
- package/dist/core/builder.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/database.d.ts +43 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +127 -0
- package/dist/database.js.map +1 -0
- package/dist/generated/prisma/browser.d.ts +15 -0
- package/dist/generated/prisma/browser.d.ts.map +1 -0
- package/dist/generated/prisma/browser.js +18 -0
- package/dist/generated/prisma/browser.js.map +1 -0
- package/dist/generated/prisma/client.d.ts +32 -0
- package/dist/generated/prisma/client.d.ts.map +1 -0
- package/dist/generated/prisma/client.js +33 -0
- package/dist/generated/prisma/client.js.map +1 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +166 -0
- package/dist/generated/prisma/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/prisma/commonInputTypes.js +11 -0
- package/dist/generated/prisma/commonInputTypes.js.map +1 -0
- package/dist/generated/prisma/enums.d.ts +2 -0
- package/dist/generated/prisma/enums.d.ts.map +1 -0
- package/dist/generated/prisma/enums.js +11 -0
- package/dist/generated/prisma/enums.js.map +1 -0
- package/dist/generated/prisma/internal/class.d.ts +138 -0
- package/dist/generated/prisma/internal/class.d.ts.map +1 -0
- package/dist/generated/prisma/internal/class.js +50 -0
- package/dist/generated/prisma/internal/class.js.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +591 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +96 -0
- package/dist/generated/prisma/internal/prismaNamespace.js.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +56 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +67 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/generated/prisma/models/User.d.ts +1169 -0
- package/dist/generated/prisma/models/User.d.ts.map +1 -0
- package/dist/generated/prisma/models/User.js +2 -0
- package/dist/generated/prisma/models/User.js.map +1 -0
- package/dist/generated/prisma/models/UserInfo.d.ts +1101 -0
- package/dist/generated/prisma/models/UserInfo.d.ts.map +1 -0
- package/dist/generated/prisma/models/UserInfo.js +2 -0
- package/dist/generated/prisma/models/UserInfo.js.map +1 -0
- package/dist/generated/prisma/models.d.ts +4 -0
- package/dist/generated/prisma/models.d.ts.map +1 -0
- package/dist/generated/prisma/models.js +2 -0
- package/dist/generated/prisma/models.js.map +1 -0
- package/dist/handlers/idle/index.d.ts +2 -0
- package/dist/handlers/idle/index.d.ts.map +1 -0
- package/dist/handlers/idle/index.js +22 -0
- package/dist/handlers/idle/index.js.map +1 -0
- package/dist/handlers/index.d.ts +12 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +14 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/menu/index.d.ts +2 -0
- package/dist/handlers/menu/index.d.ts.map +1 -0
- package/dist/handlers/menu/index.js +35 -0
- package/dist/handlers/menu/index.js.map +1 -0
- package/dist/handlers/menu.d.ts +2 -0
- package/dist/handlers/menu.d.ts.map +1 -0
- package/dist/handlers/menu.js +37 -0
- package/dist/handlers/menu.js.map +1 -0
- package/dist/handlers/welcome/index.d.ts +2 -0
- package/dist/handlers/welcome/index.d.ts.map +1 -0
- package/dist/handlers/welcome/index.js +22 -0
- package/dist/handlers/welcome/index.js.map +1 -0
- package/dist/handlers/welcome.d.ts +2 -0
- package/dist/handlers/welcome.d.ts.map +1 -0
- package/dist/handlers/welcome.js +30 -0
- package/dist/handlers/welcome.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/README.md.ejs +6 -5
- package/dist/templates/handler.ts.ejs +71 -8
- package/dist/templates/package.json.ejs +7 -0
- package/dist/templates/templates/README.md.ejs +12 -11
- package/dist/templates/templates/handler.ts.ejs +70 -8
- package/dist/templates/templates/package.json.ejs +7 -0
- package/package.json +17 -38
- package/src/templates/README.md.ejs +12 -11
- package/src/templates/handler.ts.ejs +70 -8
- package/src/templates/package.json.ejs +7 -0
|
@@ -17,7 +17,23 @@ appBuilder
|
|
|
17
17
|
// Called when user enters this state
|
|
18
18
|
// Can optionally return a state name to immediately transition
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// Access Grammy context via context.ctx
|
|
21
|
+
// See Grammy docs: https://grammy.dev/guide/context
|
|
22
|
+
await context.ctx.reply("Hello from <%= stateName %> state!");
|
|
23
|
+
|
|
24
|
+
// === INLINE KEYBOARD EXAMPLE ===
|
|
25
|
+
// import { InlineKeyboard } from 'grammy';
|
|
26
|
+
// const keyboard = new InlineKeyboard()
|
|
27
|
+
// .text('Button 1', 'btn1')
|
|
28
|
+
// .text('Button 2', 'btn2');
|
|
29
|
+
// await context.ctx.reply('Choose:', { reply_markup: keyboard });
|
|
30
|
+
|
|
31
|
+
// === POLL EXAMPLE ===
|
|
32
|
+
// await context.ctx.replyWithPoll(
|
|
33
|
+
// 'Question?',
|
|
34
|
+
// ['Option A', 'Option B', 'Option C'],
|
|
35
|
+
// { is_anonymous: false }
|
|
36
|
+
// );
|
|
21
37
|
|
|
22
38
|
// Database helpers (see src/lib/database.ts):
|
|
23
39
|
// import { getUserByTelegramId, createOrUpdateUser } from '../../lib/database.js';
|
|
@@ -30,20 +46,66 @@ appBuilder
|
|
|
30
46
|
<% } %>
|
|
31
47
|
})
|
|
32
48
|
<% if (transitionStates.length > 0) { %>
|
|
33
|
-
.onResponse(async (context: AppContext
|
|
49
|
+
.onResponse(async (context: AppContext): <%= pascalCase(stateName) %>Transitions => {
|
|
34
50
|
<% } else { %>
|
|
35
|
-
.onResponse(async (context: AppContext
|
|
51
|
+
.onResponse(async (context: AppContext) => {
|
|
36
52
|
<% } %>
|
|
37
|
-
// Called when user sends
|
|
53
|
+
// Called when user sends any update in this state (message, callback, poll, etc.)
|
|
38
54
|
// Return a state name to transition, or nothing to stay
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
// Handle different update types via Grammy context (context.ctx):
|
|
57
|
+
// - context.ctx.message?.text - text messages
|
|
58
|
+
// - context.ctx.callbackQuery?.data - inline button callbacks
|
|
59
|
+
// - context.ctx.message?.photo - photo messages
|
|
60
|
+
// - context.ctx.pollAnswer - poll responses
|
|
61
|
+
// See Grammy docs: https://grammy.dev/guide/context
|
|
62
|
+
|
|
63
|
+
// === INLINE KEYBOARD CALLBACK EXAMPLE ===
|
|
64
|
+
// if (context.ctx.callbackQuery?.data) {
|
|
65
|
+
// await context.ctx.answerCallbackQuery();
|
|
66
|
+
// const data = context.ctx.callbackQuery.data;
|
|
67
|
+
// switch (data) {
|
|
68
|
+
// case 'btn1':
|
|
69
|
+
// await context.ctx.reply('You clicked Button 1!');
|
|
70
|
+
// break;
|
|
71
|
+
// case 'btn2':
|
|
72
|
+
// return 'otherState'; // Transition to another state
|
|
73
|
+
// }
|
|
74
|
+
// return;
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
// === POLL ANSWER EXAMPLE ===
|
|
78
|
+
// if (context.ctx.pollAnswer) {
|
|
79
|
+
// const optionIds = context.ctx.pollAnswer.option_ids;
|
|
80
|
+
// const options = ['Option A', 'Option B', 'Option C'];
|
|
81
|
+
// const selected = optionIds.map(id => options[id]).join(', ');
|
|
82
|
+
// await context.ctx.reply(`You voted for: ${selected}`);
|
|
83
|
+
// return;
|
|
84
|
+
// }
|
|
85
|
+
|
|
86
|
+
// === COMMAND HANDLING EXAMPLE ===
|
|
87
|
+
// const text = context.ctx.message?.text?.trim();
|
|
88
|
+
// if (text?.startsWith('/')) {
|
|
89
|
+
// const command = text.split(' ')[0].toLowerCase();
|
|
90
|
+
// switch (command) {
|
|
91
|
+
// case '/start':
|
|
92
|
+
// await context.ctx.reply('Welcome!');
|
|
93
|
+
// return 'welcome';
|
|
94
|
+
// case '/menu':
|
|
95
|
+
// return 'menu';
|
|
96
|
+
// default:
|
|
97
|
+
// await context.ctx.reply(`Unknown command: ${command}`);
|
|
98
|
+
// }
|
|
99
|
+
// return;
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
const text = context.ctx.message?.text?.trim();
|
|
103
|
+
if (text) {
|
|
104
|
+
await context.ctx.reply(`You said: ${text}`);
|
|
105
|
+
}
|
|
41
106
|
<% if (transitionStates.length > 0) { %>
|
|
42
107
|
// Available transitions: <%= transitionStates.join(', ') %>
|
|
43
108
|
<% } %>
|
|
44
|
-
// Handle user response
|
|
45
|
-
await context.send(`You said: ${text}`);
|
|
46
|
-
|
|
47
109
|
// Database helpers (see src/lib/database.ts):
|
|
48
110
|
// import { updateUserState } from '../../lib/database.js';
|
|
49
111
|
// await updateUserState(String(context.telegramId), 'nextState', { lastMessage: text });
|
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "tsx watch src/index.ts",
|
|
7
|
+
"dev:webhook": "BOT_MODE=webhook tsx watch src/index.ts",
|
|
7
8
|
"build": "tsc",
|
|
8
9
|
"start": "node dist/index.js",
|
|
10
|
+
"start:webhook": "BOT_MODE=webhook node dist/index.js",
|
|
9
11
|
"db:generate": "prisma generate",
|
|
10
12
|
"db:migrate": "prisma migrate dev",
|
|
13
|
+
"db:deploy": "prisma migrate deploy",
|
|
14
|
+
"db:push": "prisma db push",
|
|
11
15
|
"db:studio": "prisma studio",
|
|
16
|
+
"webhook:set": "tsx scripts/set-webhook.ts",
|
|
17
|
+
"webhook:delete": "tsx scripts/delete-webhook.ts",
|
|
18
|
+
"webhook:info": "tsx scripts/webhook-info.ts",
|
|
12
19
|
"state:add": "telemeister state:add",
|
|
13
20
|
"state:delete": "telemeister state:delete",
|
|
14
21
|
"state:sync": "telemeister state:sync",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "telemeister",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "TypeScript Telegram Bot Boilerplate with XState",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|
|
@@ -33,40 +33,9 @@
|
|
|
33
33
|
"README.md",
|
|
34
34
|
"LICENSE"
|
|
35
35
|
],
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
"dev:watch": "tsx watch src/index.ts",
|
|
40
|
-
"dev:webhook": "BOT_MODE=webhook tsx src/index.ts",
|
|
41
|
-
"dev:webhook:watch": "BOT_MODE=webhook tsx watch src/index.ts",
|
|
42
|
-
"start": "node dist/index.js",
|
|
43
|
-
"start:webhook": "BOT_MODE=webhook node dist/index.js",
|
|
44
|
-
"db:generate": "prisma generate",
|
|
45
|
-
"db:migrate": "prisma migrate dev",
|
|
46
|
-
"db:deploy": "prisma migrate deploy",
|
|
47
|
-
"db:push": "prisma db push",
|
|
48
|
-
"db:studio": "prisma studio",
|
|
49
|
-
"webhook:set": "tsx scripts/set-webhook.ts",
|
|
50
|
-
"webhook:delete": "tsx scripts/delete-webhook.ts",
|
|
51
|
-
"webhook:info": "tsx scripts/webhook-info.ts",
|
|
52
|
-
"telemeister:state:add": "tsx src/cli/cli.ts state:add",
|
|
53
|
-
"telemeister:state:delete": "tsx src/cli/cli.ts state:delete",
|
|
54
|
-
"telemeister:state:sync": "tsx src/cli/cli.ts state:sync",
|
|
55
|
-
"telemeister:state:transition:add": "tsx src/cli/cli.ts state:transition:add",
|
|
56
|
-
"telemeister:state:transition:delete": "tsx src/cli/cli.ts state:transition:delete",
|
|
57
|
-
"telemeister:create-bot": "tsx src/cli/cli.ts create-bot",
|
|
58
|
-
"state:add": "telemeister state:add",
|
|
59
|
-
"state:delete": "telemeister state:delete",
|
|
60
|
-
"state:sync": "telemeister state:sync",
|
|
61
|
-
"state:transition:add": "telemeister state:transition:add",
|
|
62
|
-
"state:transition:delete": "telemeister state:transition:delete",
|
|
63
|
-
"prepublishOnly": "npm run build",
|
|
64
|
-
"prepare": "husky",
|
|
65
|
-
"lint": "eslint src/ scripts/",
|
|
66
|
-
"lint:fix": "eslint src/ scripts/ --fix",
|
|
67
|
-
"format": "prettier --write .",
|
|
68
|
-
"format:check": "prettier --check ."
|
|
69
|
-
},
|
|
36
|
+
"workspaces": [
|
|
37
|
+
"examples/*"
|
|
38
|
+
],
|
|
70
39
|
"lint-staged": {
|
|
71
40
|
"*.{ts,tsx}": [
|
|
72
41
|
"eslint --fix",
|
|
@@ -87,8 +56,7 @@
|
|
|
87
56
|
],
|
|
88
57
|
"author": "George Khromchenko",
|
|
89
58
|
"publishConfig": {
|
|
90
|
-
"access": "public"
|
|
91
|
-
"provenance": true
|
|
59
|
+
"access": "public"
|
|
92
60
|
},
|
|
93
61
|
"license": "GPL-3.0-only",
|
|
94
62
|
"dependencies": {
|
|
@@ -110,6 +78,7 @@
|
|
|
110
78
|
"@types/ejs": "^3.1.5",
|
|
111
79
|
"@types/express": "^5.0.0",
|
|
112
80
|
"@types/node": "^25.0.6",
|
|
81
|
+
"esbuild": "^0.27.3",
|
|
113
82
|
"eslint": "^9.39.2",
|
|
114
83
|
"eslint-config-prettier": "^10.1.8",
|
|
115
84
|
"husky": "^9.1.7",
|
|
@@ -119,5 +88,15 @@
|
|
|
119
88
|
"tsx": "^4.21.0",
|
|
120
89
|
"typescript": "^5.9.3",
|
|
121
90
|
"typescript-eslint": "^8.39.0"
|
|
91
|
+
},
|
|
92
|
+
"scripts": {
|
|
93
|
+
"build": "tsc && cp -r src/templates dist/templates && pnpm run build:cli",
|
|
94
|
+
"build:cli": "esbuild dist/cli/cli.js --bundle --platform=node --format=esm --outfile=bin/telemeister-cli.js --external:typescript --external:tsx --external:prisma",
|
|
95
|
+
"example:recreate": "rm -rf examples/demo-bot && tsx src/cli/cli.ts create-bot demo-bot && mv demo-bot examples/demo-bot && npm pkg set --prefix examples/demo-bot dependencies.telemeister='workspace:*' && rm -rf examples/demo-bot/node_modules examples/demo-bot/pnpm-lock.yaml examples/demo-bot/dev.db examples/demo-bot/.env",
|
|
96
|
+
"lint": "eslint src/ scripts/",
|
|
97
|
+
"lint:fix": "eslint src/ scripts/ --fix",
|
|
98
|
+
"format": "prettier --write .",
|
|
99
|
+
"format:check": "prettier --check .",
|
|
100
|
+
"release": "./scripts/release.sh"
|
|
122
101
|
}
|
|
123
|
-
}
|
|
102
|
+
}
|
|
@@ -143,9 +143,9 @@ export async function createOrder(
|
|
|
143
143
|
const user = await prisma.user.findUnique({
|
|
144
144
|
where: { telegramId },
|
|
145
145
|
});
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
if (!user) throw new Error('User not found');
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
return await prisma.order.create({
|
|
150
150
|
data: {
|
|
151
151
|
userId: user.id,
|
|
@@ -163,11 +163,11 @@ export async function getUserOrderStats(telegramId: string) {
|
|
|
163
163
|
orders: true,
|
|
164
164
|
},
|
|
165
165
|
});
|
|
166
|
-
|
|
166
|
+
|
|
167
167
|
if (!user) return { count: 0, total: 0 };
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
const total = user.orders.reduce((sum, order) => sum + order.amount, 0);
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
return {
|
|
172
172
|
count: user.orders.length,
|
|
173
173
|
total,
|
|
@@ -181,6 +181,7 @@ Use in handlers:
|
|
|
181
181
|
|
|
182
182
|
```typescript
|
|
183
183
|
import { appBuilder, type AppContext } from 'telemeister/core';
|
|
184
|
+
import type { Context } from 'grammy';
|
|
184
185
|
import type { MenuTransitions } from '../../bot-state-types.js';
|
|
185
186
|
import { getUserWithOrders, createOrder } from '../../lib/database.js';
|
|
186
187
|
|
|
@@ -188,17 +189,17 @@ appBuilder
|
|
|
188
189
|
.forState('menu')
|
|
189
190
|
.onEnter(async (context: AppContext): MenuTransitions => {
|
|
190
191
|
const user = await getUserWithOrders(String(context.telegramId));
|
|
191
|
-
|
|
192
|
+
|
|
192
193
|
if (user?.orders.length) {
|
|
193
|
-
await context.
|
|
194
|
+
await context.ctx.reply(`You have ${user.orders.length} orders!`);
|
|
194
195
|
} else {
|
|
195
|
-
await context.
|
|
196
|
+
await context.ctx.reply('Welcome! You have no orders yet.');
|
|
196
197
|
}
|
|
197
198
|
})
|
|
198
|
-
.onResponse(async (context: AppContext,
|
|
199
|
-
if (
|
|
199
|
+
.onResponse(async (context: AppContext, ctx: Context): MenuTransitions => {
|
|
200
|
+
if (ctx.message?.text === 'order') {
|
|
200
201
|
await createOrder(String(context.telegramId), 'Product A', 99.99);
|
|
201
|
-
await
|
|
202
|
+
await ctx.reply('Order created!');
|
|
202
203
|
}
|
|
203
204
|
});
|
|
204
205
|
|
|
@@ -17,7 +17,23 @@ appBuilder
|
|
|
17
17
|
// Called when user enters this state
|
|
18
18
|
// Can optionally return a state name to immediately transition
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// Access Grammy context via context.ctx
|
|
21
|
+
// See Grammy docs: https://grammy.dev/guide/context
|
|
22
|
+
await context.ctx.reply("Hello from <%= stateName %> state!");
|
|
23
|
+
|
|
24
|
+
// === INLINE KEYBOARD EXAMPLE ===
|
|
25
|
+
// import { InlineKeyboard } from 'grammy';
|
|
26
|
+
// const keyboard = new InlineKeyboard()
|
|
27
|
+
// .text('Button 1', 'btn1')
|
|
28
|
+
// .text('Button 2', 'btn2');
|
|
29
|
+
// await context.ctx.reply('Choose:', { reply_markup: keyboard });
|
|
30
|
+
|
|
31
|
+
// === POLL EXAMPLE ===
|
|
32
|
+
// await context.ctx.replyWithPoll(
|
|
33
|
+
// 'Question?',
|
|
34
|
+
// ['Option A', 'Option B', 'Option C'],
|
|
35
|
+
// { is_anonymous: false }
|
|
36
|
+
// );
|
|
21
37
|
|
|
22
38
|
// Database helpers (see src/lib/database.ts):
|
|
23
39
|
// import { getUserByTelegramId, createOrUpdateUser } from '../../lib/database.js';
|
|
@@ -30,20 +46,66 @@ appBuilder
|
|
|
30
46
|
<% } %>
|
|
31
47
|
})
|
|
32
48
|
<% if (transitionStates.length > 0) { %>
|
|
33
|
-
.onResponse(async (context: AppContext
|
|
49
|
+
.onResponse(async (context: AppContext): <%= pascalCase(stateName) %>Transitions => {
|
|
34
50
|
<% } else { %>
|
|
35
|
-
.onResponse(async (context: AppContext
|
|
51
|
+
.onResponse(async (context: AppContext) => {
|
|
36
52
|
<% } %>
|
|
37
|
-
// Called when user sends
|
|
53
|
+
// Called when user sends any update in this state (message, callback, poll, etc.)
|
|
38
54
|
// Return a state name to transition, or nothing to stay
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
// Handle different update types via Grammy context (context.ctx):
|
|
57
|
+
// - context.ctx.message?.text - text messages
|
|
58
|
+
// - context.ctx.callbackQuery?.data - inline button callbacks
|
|
59
|
+
// - context.ctx.message?.photo - photo messages
|
|
60
|
+
// - context.ctx.pollAnswer - poll responses
|
|
61
|
+
// See Grammy docs: https://grammy.dev/guide/context
|
|
62
|
+
|
|
63
|
+
// === INLINE KEYBOARD CALLBACK EXAMPLE ===
|
|
64
|
+
// if (context.ctx.callbackQuery?.data) {
|
|
65
|
+
// await context.ctx.answerCallbackQuery();
|
|
66
|
+
// const data = context.ctx.callbackQuery.data;
|
|
67
|
+
// switch (data) {
|
|
68
|
+
// case 'btn1':
|
|
69
|
+
// await context.ctx.reply('You clicked Button 1!');
|
|
70
|
+
// break;
|
|
71
|
+
// case 'btn2':
|
|
72
|
+
// return 'otherState'; // Transition to another state
|
|
73
|
+
// }
|
|
74
|
+
// return;
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
// === POLL ANSWER EXAMPLE ===
|
|
78
|
+
// if (context.ctx.pollAnswer) {
|
|
79
|
+
// const optionIds = context.ctx.pollAnswer.option_ids;
|
|
80
|
+
// const options = ['Option A', 'Option B', 'Option C'];
|
|
81
|
+
// const selected = optionIds.map(id => options[id]).join(', ');
|
|
82
|
+
// await context.ctx.reply(`You voted for: ${selected}`);
|
|
83
|
+
// return;
|
|
84
|
+
// }
|
|
85
|
+
|
|
86
|
+
// === COMMAND HANDLING EXAMPLE ===
|
|
87
|
+
// const text = context.ctx.message?.text?.trim();
|
|
88
|
+
// if (text?.startsWith('/')) {
|
|
89
|
+
// const command = text.split(' ')[0].toLowerCase();
|
|
90
|
+
// switch (command) {
|
|
91
|
+
// case '/start':
|
|
92
|
+
// await context.ctx.reply('Welcome!');
|
|
93
|
+
// return 'welcome';
|
|
94
|
+
// case '/menu':
|
|
95
|
+
// return 'menu';
|
|
96
|
+
// default:
|
|
97
|
+
// await context.ctx.reply(`Unknown command: ${command}`);
|
|
98
|
+
// }
|
|
99
|
+
// return;
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
const text = context.ctx.message?.text?.trim();
|
|
103
|
+
if (text) {
|
|
104
|
+
await context.ctx.reply(`You said: ${text}`);
|
|
105
|
+
}
|
|
41
106
|
<% if (transitionStates.length > 0) { %>
|
|
42
107
|
// Available transitions: <%= transitionStates.join(', ') %>
|
|
43
108
|
<% } %>
|
|
44
|
-
// Handle user response
|
|
45
|
-
await context.send(`You said: ${text}`);
|
|
46
|
-
|
|
47
109
|
// Database helpers (see src/lib/database.ts):
|
|
48
110
|
// import { updateUserState } from '../../lib/database.js';
|
|
49
111
|
// await updateUserState(String(context.telegramId), 'nextState', { lastMessage: text });
|
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "tsx watch src/index.ts",
|
|
7
|
+
"dev:webhook": "BOT_MODE=webhook tsx watch src/index.ts",
|
|
7
8
|
"build": "tsc",
|
|
8
9
|
"start": "node dist/index.js",
|
|
10
|
+
"start:webhook": "BOT_MODE=webhook node dist/index.js",
|
|
9
11
|
"db:generate": "prisma generate",
|
|
10
12
|
"db:migrate": "prisma migrate dev",
|
|
13
|
+
"db:deploy": "prisma migrate deploy",
|
|
14
|
+
"db:push": "prisma db push",
|
|
11
15
|
"db:studio": "prisma studio",
|
|
16
|
+
"webhook:set": "tsx scripts/set-webhook.ts",
|
|
17
|
+
"webhook:delete": "tsx scripts/delete-webhook.ts",
|
|
18
|
+
"webhook:info": "tsx scripts/webhook-info.ts",
|
|
12
19
|
"state:add": "telemeister state:add",
|
|
13
20
|
"state:delete": "telemeister state:delete",
|
|
14
21
|
"state:sync": "telemeister state:sync",
|