telemeister 0.2.8 → 0.2.10

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.
@@ -1069,6 +1069,12 @@ function getPackageRoot2() {
1069
1069
  }
1070
1070
  return path3.join(currentDir, "..");
1071
1071
  }
1072
+ function getPackageVersion() {
1073
+ const packageRoot = getPackageRoot2();
1074
+ const packageJsonPath = path3.join(packageRoot, "package.json");
1075
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1076
+ return packageJson.version;
1077
+ }
1072
1078
  function loadTemplate(templateName) {
1073
1079
  const packageRoot = getPackageRoot2();
1074
1080
  const templatePath = path3.join(packageRoot, "dist", "templates", templateName);
@@ -1119,7 +1125,7 @@ async function createBot(botName) {
1119
1125
  fs3.mkdirSync(path3.join(targetDir, "src", "lib"), { recursive: true });
1120
1126
  fs3.writeFileSync(path3.join(targetDir, "src", "lib", "database.ts"), loadTemplate("database.ts.ejs"));
1121
1127
  fs3.writeFileSync(path3.join(targetDir, "README.md"), renderTemplate("README.md.ejs", { botName }));
1122
- fs3.writeFileSync(path3.join(targetDir, "package.json"), renderTemplate("package.json.ejs", { botName }));
1128
+ fs3.writeFileSync(path3.join(targetDir, "package.json"), renderTemplate("package.json.ejs", { botName, telemeisterVersion: getPackageVersion() }));
1123
1129
  process.chdir(targetDir);
1124
1130
  await stateSync();
1125
1131
  console.log("\n\u{1F4E6} Installing dependencies...");
@@ -1 +1 @@
1
- {"version":3,"file":"create-bot.d.ts","sourceRoot":"","sources":["../../src/cli/create-bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4CH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA6G1E"}
1
+ {"version":3,"file":"create-bot.d.ts","sourceRoot":"","sources":["../../src/cli/create-bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+CH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA6G1E"}
@@ -8,18 +8,20 @@ import { execSync } from 'child_process';
8
8
  import ejs from 'ejs';
9
9
  import { stateSync } from './state-manager.js';
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
- // Get the package root directory (works for both dist/cli/ and bin/ locations)
12
11
  function getPackageRoot() {
13
- // __dirname is either dist/cli/ (from tsc) or bin/ (from bundle)
14
12
  const currentDir = __dirname;
15
- // If we're in dist/cli/, go up 2 levels to get package root
16
- // If we're in bin/, go up 1 level to get package root
17
13
  const baseName = path.basename(currentDir);
18
14
  if (baseName === 'cli' || baseName === 'dist') {
19
15
  return path.join(currentDir, '..', '..');
20
16
  }
21
17
  return path.join(currentDir, '..');
22
18
  }
19
+ function getPackageVersion() {
20
+ const packageRoot = getPackageRoot();
21
+ const packageJsonPath = path.join(packageRoot, 'package.json');
22
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
23
+ return packageJson.version;
24
+ }
23
25
  function loadTemplate(templateName) {
24
26
  const packageRoot = getPackageRoot();
25
27
  const templatePath = path.join(packageRoot, 'dist', 'templates', templateName);
@@ -75,7 +77,7 @@ export async function createBot(botName) {
75
77
  // Note: Bot runtime files (session.ts, polling.ts, webhook.ts) are now provided by the framework
76
78
  // in 'telemeister/core/bot' and don't need to be generated
77
79
  fs.writeFileSync(path.join(targetDir, 'README.md'), renderTemplate('README.md.ejs', { botName }));
78
- fs.writeFileSync(path.join(targetDir, 'package.json'), renderTemplate('package.json.ejs', { botName }));
80
+ fs.writeFileSync(path.join(targetDir, 'package.json'), renderTemplate('package.json.ejs', { botName, telemeisterVersion: getPackageVersion() }));
79
81
  // Sync handlers and types from bot.json
80
82
  process.chdir(targetDir);
81
83
  await stateSync();
@@ -1 +1 @@
1
- {"version":3,"file":"create-bot.js","sourceRoot":"","sources":["../../src/cli/create-bot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-bot.js","sourceRoot":"","sources":["../../src/cli/create-bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,SAAS,cAAc;IACrB,MAAM,UAAU,GAAG,SAAS,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1E,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB;IACxC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;IAC/E,OAAO,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB,EAAE,OAAgC,EAAE;IAC9E,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAA2B;IACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CACX,wGAAwG,CACzG,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC;IACjE,MAAM,KAAK,GAAG,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAErD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;IAEvD,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,uBAAuB,OAAO,kBAAkB,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wBAAwB,OAAO,IAAI,CAAC,CAAC;IAEjD,6BAA6B;IAC7B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElE,8BAA8B;IAC9B,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC;IACpF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC3F,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACxF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC;IACjF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC;IACxF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,EAC/C,YAAY,CAAC,0BAA0B,CAAC,CACzC,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,YAAY,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAEjG,uBAAuB;IACvB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,EACjD,YAAY,CAAC,iBAAiB,CAAC,CAChC,CAAC;IAEF,iGAAiG;IACjG,2DAA2D;IAE3D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,cAAc,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAClG,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EACpC,cAAc,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,EAAE,CAAC,CACzF,CAAC;IAEF,wCAAwC;IACxC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzB,MAAM,SAAS,EAAE,CAAC;IAElB,+BAA+B;IAC/B,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,iDAAiD,SAAS,eAAe,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,eAAe,CAAC;IAElC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,QAAQ,CAAC,GAAG,KAAK,cAAc,EAAE;YAC/B,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE;SACjD,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CACX,mDAAmD,KAAK,2BAA2B,CACpF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,QAAQ,CAAC,oCAAoC,EAAE;YAC7C,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE;SACjD,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CACX,sDAAsD,KAAK,0BAA0B,CACtF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,2BAA2B,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;AAChC,CAAC"}
@@ -14,8 +14,8 @@ declare class StateBuilder<TState extends BotState = BotState> {
14
14
  * Can optionally return a state name to immediately transition to
15
15
  *
16
16
  * Example:
17
- * .onEnter(async (ctx) => {
18
- * await ctx.send('Welcome!');
17
+ * .onEnter(async (context) => {
18
+ * await context.ctx.reply('Welcome!');
19
19
  * // Optionally transition immediately:
20
20
  * return 'anotherState';
21
21
  * })
@@ -23,11 +23,11 @@ declare class StateBuilder<TState extends BotState = BotState> {
23
23
  onEnter(handler: EnterHandler<TState>): this;
24
24
  /**
25
25
  * Set the onResponse handler for this state
26
- * Called when the user sends a message while in this state
26
+ * Called when the user sends any update while in this state
27
27
  *
28
28
  * Example:
29
- * .onResponse(async (ctx, response) => {
30
- * if (response === 'yes') {
29
+ * .onResponse(async (context) => {
30
+ * if (context.ctx.message?.text === 'yes') {
31
31
  * return 'confirmed';
32
32
  * }
33
33
  * return 'cancelled';
@@ -62,11 +62,11 @@ declare class MultiStateBuilder<TState extends BotState = BotState> {
62
62
  *
63
63
  * typedBuilder
64
64
  * .forState('welcome')
65
- * .onEnter(async (ctx) => {
66
- * await ctx.send('Welcome!');
65
+ * .onEnter(async (context) => {
66
+ * await context.ctx.reply('Welcome!');
67
67
  * return 'menu'; // ✅ Type-safe: only 'idle' | 'welcome' | 'menu' | 'collectName' allowed
68
68
  * })
69
- * .onResponse(async (ctx, response) => {
69
+ * .onResponse(async (context) => {
70
70
  * return 'collectName'; // ✅ Also type-safe
71
71
  * });
72
72
  * ```
@@ -16,8 +16,8 @@ class StateBuilder {
16
16
  * Can optionally return a state name to immediately transition to
17
17
  *
18
18
  * Example:
19
- * .onEnter(async (ctx) => {
20
- * await ctx.send('Welcome!');
19
+ * .onEnter(async (context) => {
20
+ * await context.ctx.reply('Welcome!');
21
21
  * // Optionally transition immediately:
22
22
  * return 'anotherState';
23
23
  * })
@@ -30,11 +30,11 @@ class StateBuilder {
30
30
  }
31
31
  /**
32
32
  * Set the onResponse handler for this state
33
- * Called when the user sends a message while in this state
33
+ * Called when the user sends any update while in this state
34
34
  *
35
35
  * Example:
36
- * .onResponse(async (ctx, response) => {
37
- * if (response === 'yes') {
36
+ * .onResponse(async (context) => {
37
+ * if (context.ctx.message?.text === 'yes') {
38
38
  * return 'confirmed';
39
39
  * }
40
40
  * return 'cancelled';
@@ -91,11 +91,11 @@ class MultiStateBuilder {
91
91
  *
92
92
  * typedBuilder
93
93
  * .forState('welcome')
94
- * .onEnter(async (ctx) => {
95
- * await ctx.send('Welcome!');
94
+ * .onEnter(async (context) => {
95
+ * await context.ctx.reply('Welcome!');
96
96
  * return 'menu'; // ✅ Type-safe: only 'idle' | 'welcome' | 'menu' | 'collectName' allowed
97
97
  * })
98
- * .onResponse(async (ctx, response) => {
98
+ * .onResponse(async (context) => {
99
99
  * return 'collectName'; // ✅ Also type-safe
100
100
  * });
101
101
  * ```
@@ -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,
@@ -189,7 +189,7 @@ appBuilder
189
189
  .forState('menu')
190
190
  .onEnter(async (context: AppContext): MenuTransitions => {
191
191
  const user = await getUserWithOrders(String(context.telegramId));
192
-
192
+
193
193
  if (user?.orders.length) {
194
194
  await context.ctx.reply(`You have ${user.orders.length} orders!`);
195
195
  } else {
@@ -1,5 +1,4 @@
1
1
  import { appBuilder, type AppContext } from 'telemeister/core';
2
- import type { Context } from 'grammy';
3
2
  <% if (transitionStates.length > 0) { %>import type { <%= pascalCase(stateName) %>Transitions } from '../../bot-state-types.js';<% } %>
4
3
 
5
4
  /**
@@ -47,27 +46,27 @@ appBuilder
47
46
  <% } %>
48
47
  })
49
48
  <% if (transitionStates.length > 0) { %>
50
- .onResponse(async (context: AppContext, ctx: Context): <%= pascalCase(stateName) %>Transitions => {
49
+ .onResponse(async (context: AppContext): <%= pascalCase(stateName) %>Transitions => {
51
50
  <% } else { %>
52
- .onResponse(async (context: AppContext, ctx: Context) => {
51
+ .onResponse(async (context: AppContext) => {
53
52
  <% } %>
54
53
  // Called when user sends any update in this state (message, callback, poll, etc.)
55
54
  // Return a state name to transition, or nothing to stay
56
55
 
57
- // Handle different update types via Grammy context:
58
- // - ctx.message?.text - text messages
59
- // - ctx.callbackQuery?.data - inline button callbacks
60
- // - ctx.message?.photo - photo messages
61
- // - ctx.pollAnswer - poll responses
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
62
61
  // See Grammy docs: https://grammy.dev/guide/context
63
62
 
64
63
  // === INLINE KEYBOARD CALLBACK EXAMPLE ===
65
- // if (ctx.callbackQuery?.data) {
66
- // await ctx.answerCallbackQuery();
67
- // const data = ctx.callbackQuery.data;
64
+ // if (context.ctx.callbackQuery?.data) {
65
+ // await context.ctx.answerCallbackQuery();
66
+ // const data = context.ctx.callbackQuery.data;
68
67
  // switch (data) {
69
68
  // case 'btn1':
70
- // await ctx.reply('You clicked Button 1!');
69
+ // await context.ctx.reply('You clicked Button 1!');
71
70
  // break;
72
71
  // case 'btn2':
73
72
  // return 'otherState'; // Transition to another state
@@ -76,33 +75,33 @@ appBuilder
76
75
  // }
77
76
 
78
77
  // === POLL ANSWER EXAMPLE ===
79
- // if (ctx.pollAnswer) {
80
- // const optionIds = ctx.pollAnswer.option_ids;
78
+ // if (context.ctx.pollAnswer) {
79
+ // const optionIds = context.ctx.pollAnswer.option_ids;
81
80
  // const options = ['Option A', 'Option B', 'Option C'];
82
81
  // const selected = optionIds.map(id => options[id]).join(', ');
83
- // await ctx.reply(`You voted for: ${selected}`);
82
+ // await context.ctx.reply(`You voted for: ${selected}`);
84
83
  // return;
85
84
  // }
86
85
 
87
86
  // === COMMAND HANDLING EXAMPLE ===
88
- // const text = ctx.message?.text?.trim();
87
+ // const text = context.ctx.message?.text?.trim();
89
88
  // if (text?.startsWith('/')) {
90
89
  // const command = text.split(' ')[0].toLowerCase();
91
90
  // switch (command) {
92
91
  // case '/start':
93
- // await ctx.reply('Welcome!');
92
+ // await context.ctx.reply('Welcome!');
94
93
  // return 'welcome';
95
94
  // case '/menu':
96
95
  // return 'menu';
97
96
  // default:
98
- // await ctx.reply(`Unknown command: ${command}`);
97
+ // await context.ctx.reply(`Unknown command: ${command}`);
99
98
  // }
100
99
  // return;
101
100
  // }
102
101
 
103
- const text = ctx.message?.text?.trim();
102
+ const text = context.ctx.message?.text?.trim();
104
103
  if (text) {
105
- await ctx.reply(`You said: ${text}`);
104
+ await context.ctx.reply(`You said: ${text}`);
106
105
  }
107
106
  <% if (transitionStates.length > 0) { %>
108
107
  // Available transitions: <%= transitionStates.join(', ') %>
@@ -29,7 +29,7 @@
29
29
  "dotenv": "^16.4.7",
30
30
  "express": "^4.21.2",
31
31
  "grammy": "^1.35.1",
32
- "telemeister": "^0.1.8"
32
+ "telemeister": "^<%= telemeisterVersion %>"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/express": "^5.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telemeister",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "TypeScript Telegram Bot Boilerplate with XState",
5
5
  "type": "module",
6
6
  "main": "./dist/core/index.js",
@@ -90,7 +90,7 @@
90
90
  "typescript-eslint": "^8.39.0"
91
91
  },
92
92
  "scripts": {
93
- "build": "tsc && cp -r src/templates dist/templates && pnpm run build:cli",
93
+ "build": "tsc && rm -rf dist/templates && cp -r src/templates dist/templates && pnpm run build:cli",
94
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
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
96
  "lint": "eslint src/ scripts/",
@@ -29,7 +29,7 @@
29
29
  "dotenv": "^16.4.7",
30
30
  "express": "^4.21.2",
31
31
  "grammy": "^1.35.1",
32
- "telemeister": "^0.1.8"
32
+ "telemeister": "^<%= telemeisterVersion %>"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/express": "^5.0.0",
@@ -1,286 +0,0 @@
1
- # <%= botName %>
2
-
3
- Telegram bot built with Telemeister - a TypeScript framework for building stateful Telegram bots with Prisma ORM.
4
-
5
- ## Quick Start
6
-
7
- ```bash
8
- # Install dependencies
9
- npm install
10
-
11
- # Setup environment
12
- cp .env.example .env
13
- # Edit .env and add your bot token
14
-
15
- # Setup database
16
- npm run db:generate
17
- npm run db:migrate
18
-
19
- # Run the bot
20
- npm run dev
21
- ```
22
-
23
- ## Project Structure
24
-
25
- ```
26
- <%= botName %>/
27
- ├── bot.json # State machine config (do not commit)
28
- ├── prisma/
29
- │ ├── schema.prisma # Database schema - edit to add custom models
30
- │ └── config.ts # Prisma configuration
31
- ├── src/
32
- │ ├── bot/ # Bot runners (polling/webhook modes)
33
- │ │ ├── polling.ts # Polling mode runner
34
- │ │ └── webhook.ts # Webhook mode runner
35
- │ ├── handlers/ # State handlers
36
- │ │ ├── index.ts # Auto-generated handler imports
37
- │ │ ├── welcome/ # Example: welcome state
38
- │ │ ├── menu/ # Example: menu state
39
- │ │ └── idle/ # Default idle state
40
- │ ├── lib/ # Database and utilities
41
- │ │ └── database.ts # Database adapter implementation
42
- │ ├── bot-state-types.ts # Generated types (do not edit)
43
- │ └── index.ts # Bot entry point
44
- ├── doc/
45
- │ ├── bot-diagram.md # Generated state diagram
46
- │ └── bot-diagram.png # Visual diagram
47
- └── dev.db # SQLite database (do not commit)
48
- ```
49
-
50
- ## State Management Commands
51
-
52
- Add and manage conversation states:
53
-
54
- ```bash
55
- # Add a new state (creates handler + updates types)
56
- npm run state:add settings
57
-
58
- # Delete a state
59
- npm run state:delete settings
60
-
61
- # Add transition between states
62
- npm run state:transition:add welcome settings
63
-
64
- # Remove transition
65
- npm run state:transition:delete welcome settings
66
-
67
- # Sync types and regenerate diagrams
68
- npm run state:sync
69
- ```
70
-
71
- ## Database
72
-
73
- ### Custom Models
74
-
75
- Edit `prisma/schema.prisma` to add your custom models:
76
-
77
- ```prisma
78
- model User {
79
- id Int @id @default(autoincrement())
80
- telegramId String @unique
81
- chatId String
82
- currentState String @default("idle")
83
- updatedAt DateTime @updatedAt
84
- info UserInfo?
85
- // Add your relations here:
86
- orders Order[]
87
- }
88
-
89
- model UserInfo {
90
- id Int @id @default(autoincrement())
91
- userId Int @unique
92
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
93
- stateData String @default("{}")
94
- }
95
-
96
- // Your custom model
97
- model Order {
98
- id Int @id @default(autoincrement())
99
- userId Int
100
- user User @relation(fields: [userId], references: [id])
101
- product String
102
- amount Float
103
- createdAt DateTime @default(now())
104
- }
105
- ```
106
-
107
- After editing the schema, regenerate the client:
108
-
109
- ```bash
110
- npm run db:generate # Regenerate Prisma client
111
- npm run db:migrate # Create and apply migration
112
- ```
113
-
114
- ### Custom Database Queries
115
-
116
- Create `src/lib/database.ts` for your database operations:
117
-
118
- ```typescript
119
- import { PrismaClient } from '@prisma/client';
120
-
121
- const prisma = new PrismaClient();
122
-
123
- // Example: Get user with orders
124
- export async function getUserWithOrders(telegramId: string) {
125
- return await prisma.user.findUnique({
126
- where: { telegramId },
127
- include: {
128
- info: true,
129
- orders: {
130
- orderBy: { createdAt: 'desc' },
131
- take: 10,
132
- },
133
- },
134
- });
135
- }
136
-
137
- // Example: Create a new order
138
- export async function createOrder(
139
- telegramId: string,
140
- product: string,
141
- amount: number
142
- ) {
143
- const user = await prisma.user.findUnique({
144
- where: { telegramId },
145
- });
146
-
147
- if (!user) throw new Error('User not found');
148
-
149
- return await prisma.order.create({
150
- data: {
151
- userId: user.id,
152
- product,
153
- amount,
154
- },
155
- });
156
- }
157
-
158
- // Example: Get order statistics
159
- export async function getUserOrderStats(telegramId: string) {
160
- const user = await prisma.user.findUnique({
161
- where: { telegramId },
162
- include: {
163
- orders: true,
164
- },
165
- });
166
-
167
- if (!user) return { count: 0, total: 0 };
168
-
169
- const total = user.orders.reduce((sum, order) => sum + order.amount, 0);
170
-
171
- return {
172
- count: user.orders.length,
173
- total,
174
- };
175
- }
176
-
177
- export { prisma };
178
- ```
179
-
180
- Use in handlers:
181
-
182
- ```typescript
183
- import { appBuilder, type AppContext } from 'telemeister/core';
184
- import type { Context } from 'grammy';
185
- import type { MenuTransitions } from '../../bot-state-types.js';
186
- import { getUserWithOrders, createOrder } from '../../lib/database.js';
187
-
188
- appBuilder
189
- .forState('menu')
190
- .onEnter(async (context: AppContext): MenuTransitions => {
191
- const user = await getUserWithOrders(String(context.telegramId));
192
-
193
- if (user?.orders.length) {
194
- await context.ctx.reply(`You have ${user.orders.length} orders!`);
195
- } else {
196
- await context.ctx.reply('Welcome! You have no orders yet.');
197
- }
198
- })
199
- .onResponse(async (context: AppContext, ctx: Context): MenuTransitions => {
200
- if (ctx.message?.text === 'order') {
201
- await createOrder(String(context.telegramId), 'Product A', 99.99);
202
- await ctx.reply('Order created!');
203
- }
204
- });
205
-
206
- console.log('✅ State handler registered: menu');
207
- ```
208
-
209
- ## Available Scripts
210
-
211
- | Command | Description |
212
- |---------|-------------|
213
- | `npm run dev` | Start bot in development mode (watch) |
214
- | `npm run build` | Compile TypeScript |
215
- | `npm run start` | Run compiled bot |
216
- | `npm run db:generate` | Generate Prisma client |
217
- | `npm run db:migrate` | Create and apply database migrations |
218
- | `npm run db:studio` | Open Prisma Studio (database GUI) |
219
- | `npm run state:add <name>` | Add new state |
220
- | `npm run state:delete <name>` | Delete state |
221
- | `npm run state:sync` | Sync types and handlers |
222
- | `npm run state:transition:add <from> <to>` | Add transition |
223
- | `npm run state:transition:delete <from> <to>` | Delete transition |
224
-
225
- ## Environment Variables
226
-
227
- | Variable | Required | Description |
228
- |----------|----------|-------------|
229
- | `BOT_TOKEN` | Yes | Telegram bot token from @BotFather |
230
- | `DATABASE_URL` | Yes | Database connection string |
231
- | `BOT_MODE` | No | `polling` (default) or `webhook` |
232
- | `WEBHOOK_URL` | If webhook | Webhook URL for production |
233
- | `PORT` | If webhook | Port for webhook server (default: 3000) |
234
-
235
- ## Database Configuration
236
-
237
- ### SQLite (Development)
238
- ```env
239
- DATABASE_URL=file:./dev.db
240
- ```
241
-
242
- ### MySQL (Production)
243
- ```env
244
- DATABASE_URL=mysql://user:password@localhost:3306/dbname
245
- ```
246
-
247
- When switching to MySQL, update `prisma/schema.prisma`:
248
- ```prisma
249
- datasource db {
250
- provider = "mysql"
251
- }
252
- ```
253
-
254
- ## State Machine
255
-
256
- The bot uses a finite state machine defined in `bot.json`:
257
-
258
- ```json
259
- {
260
- "welcome": ["menu"],
261
- "menu": ["welcome", "settings"],
262
- "settings": ["menu"]
263
- }
264
- ```
265
-
266
- Each handler can transition to its listed states by returning the state name.
267
-
268
- ## Development Tips
269
-
270
- 1. **Always regenerate types after schema changes**: `npm run db:generate`
271
- 2. **Use Prisma Studio for database inspection**: `npm run db:studio`
272
- 3. **Check the diagram**: See `doc/bot-diagram.md` for visual state flow
273
- 4. **State data**: Use `context.setData()` and `context.getData()` for temporary storage
274
- 5. **Persistent storage**: Use custom Prisma models for user data
275
-
276
- ## Deployment
277
-
278
- 1. Set production database URL
279
- 2. Run migrations: `npm run db:migrate`
280
- 3. Build: `npm run build`
281
- 4. Start: `npm start`
282
-
283
- For webhook mode:
284
- ```bash
285
- BOT_MODE=webhook WEBHOOK_URL=https://your-domain.com npm start
286
- ```
@@ -1,5 +0,0 @@
1
- {
2
- "idle": ["welcome"],
3
- "welcome": ["menu"],
4
- "menu": ["welcome"]
5
- }
@@ -1,138 +0,0 @@
1
- /**
2
- * Custom Database Queries
3
- *
4
- * This file contains example database operations for your bot.
5
- * Copy this file to src/lib/database.ts and customize as needed.
6
- *
7
- * The Prisma client is available from the generated client.
8
- *
9
- * IMPORTANT: After editing prisma/schema.prisma, run:
10
- * npm run db:generate # Regenerate Prisma client
11
- * npm run db:migrate # Create and apply migration
12
- */
13
-
14
- import { PrismaClient } from '@prisma/client';
15
-
16
- const prisma = new PrismaClient();
17
-
18
- /**
19
- * Example: Get user with their info and any custom relations
20
- */
21
- export async function getUserWithDetails(telegramId: string) {
22
- return await prisma.user.findUnique({
23
- where: { telegramId },
24
- include: {
25
- info: true,
26
- // Add your custom relations here after updating schema
27
- // orders: true,
28
- // profile: true,
29
- },
30
- });
31
- }
32
-
33
- /**
34
- * Example: Update user's state data
35
- */
36
- export async function updateUserData(
37
- telegramId: string,
38
- data: Record<string, unknown>
39
- ) {
40
- const user = await prisma.user.findUnique({
41
- where: { telegramId },
42
- });
43
-
44
- if (!user) {
45
- throw new Error(`User ${telegramId} not found`);
46
- }
47
-
48
- // Get existing data
49
- const existingInfo = await prisma.userInfo.findUnique({
50
- where: { userId: user.id },
51
- });
52
-
53
- const existingData = existingInfo
54
- ? JSON.parse(existingInfo.stateData)
55
- : {};
56
-
57
- // Merge and save
58
- await prisma.userInfo.upsert({
59
- where: { userId: user.id },
60
- create: {
61
- userId: user.id,
62
- stateData: JSON.stringify({ ...existingData, ...data }),
63
- },
64
- update: {
65
- stateData: JSON.stringify({ ...existingData, ...data }),
66
- },
67
- });
68
-
69
- return { ...existingData, ...data };
70
- }
71
-
72
- /**
73
- * Example: Get user data
74
- */
75
- export async function getUserData(telegramId: string) {
76
- const user = await prisma.user.findUnique({
77
- where: { telegramId },
78
- include: { info: true },
79
- });
80
-
81
- if (!user?.info) {
82
- return {};
83
- }
84
-
85
- return JSON.parse(user.info.stateData);
86
- }
87
-
88
- /**
89
- * Example: Add a custom query for your domain
90
- *
91
- * After adding an Order model to schema.prisma, uncomment and customize:
92
- */
93
- /*
94
- export async function createOrder(
95
- telegramId: string,
96
- product: string,
97
- amount: number
98
- ) {
99
- const user = await prisma.user.findUnique({
100
- where: { telegramId },
101
- });
102
-
103
- if (!user) throw new Error('User not found');
104
-
105
- return await prisma.order.create({
106
- data: {
107
- userId: user.id,
108
- product,
109
- amount,
110
- },
111
- });
112
- }
113
-
114
- export async function getUserOrders(telegramId: string) {
115
- const user = await prisma.user.findUnique({
116
- where: { telegramId },
117
- include: {
118
- orders: {
119
- orderBy: { createdAt: 'desc' },
120
- },
121
- },
122
- });
123
-
124
- return user?.orders || [];
125
- }
126
-
127
- export async function getOrderStats(telegramId: string) {
128
- const orders = await getUserOrders(telegramId);
129
-
130
- return {
131
- count: orders.length,
132
- total: orders.reduce((sum, order) => sum + order.amount, 0),
133
- };
134
- }
135
- */
136
-
137
- // Export prisma client for direct access if needed
138
- export { prisma };
@@ -1,206 +0,0 @@
1
- /**
2
- * Database Entry Point
3
- *
4
- * Implements DatabaseAdapter interface for the Telemeister framework.
5
- * Uses Prisma ORM 7.x with driver adapters for database operations.
6
- *
7
- * Switch between SQLite and MySQL by changing DATABASE_URL in .env:
8
- * - SQLite: DATABASE_URL="file:./dev.db"
9
- * - MySQL: DATABASE_URL="mysql://user:password@localhost:3306/dbname"
10
- *
11
- * Note: When switching providers, update the provider in prisma/schema.prisma:
12
- * - SQLite: provider = "sqlite"
13
- * - MySQL: provider = "mysql"
14
- */
15
-
16
- import 'dotenv/config';
17
- import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
18
- import { PrismaClient, User, UserInfo } from '../generated/prisma/client.js';
19
- import type { DatabaseAdapter, UserData } from 'telemeister/core/bot';
20
-
21
- // Determine database provider from URL
22
- const databaseUrl = process.env.DATABASE_URL || 'file:./dev.db';
23
- const isMysql = databaseUrl.startsWith('mysql:');
24
-
25
- // Create adapter based on database type
26
- // For now, SQLite is fully supported. MySQL adapter requires additional setup.
27
- const adapter = isMysql
28
- ? (() => {
29
- throw new Error(
30
- 'MySQL adapter not yet configured. Please configure @prisma/adapter-mariadb in src/lib/database.ts'
31
- );
32
- })()
33
- : new PrismaBetterSqlite3({
34
- url: databaseUrl,
35
- });
36
-
37
- // Prisma client instance with adapter
38
- const prisma = new PrismaClient({ adapter });
39
-
40
- // Re-export types from Prisma
41
- export type { User, UserInfo };
42
-
43
- // Type for user with included info relation
44
- export type UserWithInfo = User & {
45
- info: UserInfo | null;
46
- };
47
-
48
- /**
49
- * Get user by Telegram ID with joined user info
50
- */
51
- export async function getUserByTelegramId(telegramId: string): Promise<UserWithInfo | null> {
52
- const user = await prisma.user.findUnique({
53
- where: { telegramId },
54
- include: { info: true },
55
- });
56
-
57
- return user;
58
- }
59
-
60
- /**
61
- * Create or update a user
62
- */
63
- export async function createOrUpdateUser(data: {
64
- telegramId: string;
65
- chatId: string;
66
- currentState?: string;
67
- stateData?: Record<string, unknown>;
68
- }): Promise<UserWithInfo> {
69
- const existingUser = await getUserByTelegramId(data.telegramId);
70
-
71
- if (existingUser) {
72
- // Update existing user
73
- const updatedUser = await prisma.user.update({
74
- where: { telegramId: data.telegramId },
75
- data: {
76
- chatId: data.chatId,
77
- ...(data.currentState && { currentState: data.currentState }),
78
- },
79
- include: { info: true },
80
- });
81
-
82
- // Update or create user info if stateData provided
83
- if (data.stateData) {
84
- await prisma.userInfo.upsert({
85
- where: { userId: updatedUser.id },
86
- create: {
87
- userId: updatedUser.id,
88
- stateData: JSON.stringify(data.stateData),
89
- },
90
- update: {
91
- stateData: JSON.stringify(data.stateData),
92
- },
93
- });
94
-
95
- // Return updated user with info
96
- return (await getUserByTelegramId(data.telegramId))!;
97
- }
98
-
99
- return updatedUser;
100
- } else {
101
- // Create new user with info
102
- const newUser = await prisma.user.create({
103
- data: {
104
- telegramId: data.telegramId,
105
- chatId: data.chatId,
106
- currentState: data.currentState || 'idle',
107
- info: {
108
- create: {
109
- stateData: JSON.stringify(data.stateData || {}),
110
- },
111
- },
112
- },
113
- include: { info: true },
114
- });
115
-
116
- return newUser;
117
- }
118
- }
119
-
120
- /**
121
- * Update user state and optional state data
122
- */
123
- export async function updateUserState(
124
- telegramId: string,
125
- currentState: string,
126
- stateData?: Record<string, unknown>
127
- ): Promise<void> {
128
- const user = await prisma.user.findUnique({
129
- where: { telegramId },
130
- });
131
- if (!user) {
132
- throw new Error(`User with telegramId ${telegramId} not found`);
133
- }
134
-
135
- // Update user state
136
- await prisma.user.update({
137
- where: { telegramId },
138
- data: { currentState },
139
- });
140
-
141
- // Update state data if provided
142
- if (stateData) {
143
- await prisma.userInfo.upsert({
144
- where: { userId: user.id },
145
- create: {
146
- userId: user.id,
147
- stateData: JSON.stringify(stateData),
148
- },
149
- update: {
150
- stateData: JSON.stringify(stateData),
151
- },
152
- });
153
- }
154
- }
155
-
156
- /**
157
- * Disconnect from database
158
- */
159
- export async function disconnectDB(): Promise<void> {
160
- await prisma.$disconnect();
161
- }
162
-
163
- /**
164
- * Database adapter implementation for Telemeister framework
165
- * Implements the DatabaseAdapter interface
166
- */
167
- export const databaseAdapter: DatabaseAdapter = {
168
- getUserByTelegramId: async (telegramId: string): Promise<UserData | null> => {
169
- const user = await getUserByTelegramId(telegramId);
170
- if (!user) return null;
171
-
172
- return {
173
- id: user.id,
174
- telegramId: user.telegramId,
175
- chatId: user.chatId,
176
- currentState: user.currentState,
177
- info: user.info,
178
- };
179
- },
180
-
181
- createOrUpdateUser: async (data: {
182
- telegramId: string;
183
- chatId: string;
184
- currentState?: string;
185
- stateData?: Record<string, unknown>;
186
- }): Promise<UserData> => {
187
- const user = await createOrUpdateUser(data);
188
- return {
189
- id: user.id,
190
- telegramId: user.telegramId,
191
- chatId: user.chatId,
192
- currentState: user.currentState,
193
- info: user.info,
194
- };
195
- },
196
-
197
- updateUserState: async (
198
- telegramId: string,
199
- currentState: string,
200
- stateData?: Record<string, unknown>
201
- ): Promise<void> => {
202
- await updateUserState(telegramId, currentState, stateData);
203
- },
204
- };
205
-
206
- export { prisma };
@@ -1,5 +0,0 @@
1
- # Telegram Bot Configuration
2
- BOT_TOKEN=your_bot_token_here
3
-
4
- # Database (SQLite for development)
5
- DATABASE_URL=file:./dev.db
@@ -1,21 +0,0 @@
1
- # Dependencies
2
- node_modules/
3
-
4
- # Environment variables
5
- .env
6
- .env.local
7
- .env.*.local
8
-
9
- # Build output
10
- dist/
11
-
12
- # Prisma
13
- prisma/*.db
14
- prisma/*.db-journal
15
-
16
- # Logs
17
- *.log
18
-
19
- # OS
20
- .DS_Store
21
- Thumbs.db
@@ -1,114 +0,0 @@
1
- import { appBuilder, type AppContext } from 'telemeister/core';
2
- <% if (transitionStates.length > 0) { %>import type { <%= pascalCase(stateName) %>Transitions } from '../../bot-state-types.js';<% } %>
3
-
4
- /**
5
- * <%= stateName %> State Handler
6
- *
7
- * This file defines the handlers for the "<%= stateName %>" state.
8
- */
9
-
10
- appBuilder
11
- .forState('<%= stateName %>')
12
- <% if (transitionStates.length > 0) { %>
13
- .onEnter(async (context: AppContext): <%= pascalCase(stateName) %>Transitions => {
14
- <% } else { %>
15
- .onEnter(async (context: AppContext) => {
16
- <% } %>
17
- // Called when user enters this state
18
- // Can optionally return a state name to immediately transition
19
-
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
- // );
37
-
38
- // Database helpers (see src/lib/database.ts):
39
- // import { getUserByTelegramId, createOrUpdateUser } from '../../lib/database.js';
40
- // const user = await getUserByTelegramId(String(context.telegramId));
41
- <% if (transitionStates.length > 0) { %>
42
- // Available transitions: <%= transitionStates.join(', ') %>
43
- // return "<%= transitionStates[0] %>";
44
- <% } else { %>
45
- // No transitions defined - add them with: npm run state:transition:add -- <%= stateName %> <target-state>
46
- <% } %>
47
- })
48
- <% if (transitionStates.length > 0) { %>
49
- .onResponse(async (context: AppContext): <%= pascalCase(stateName) %>Transitions => {
50
- <% } else { %>
51
- .onResponse(async (context: AppContext) => {
52
- <% } %>
53
- // Called when user sends any update in this state (message, callback, poll, etc.)
54
- // Return a state name to transition, or nothing to stay
55
-
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
- }
106
- <% if (transitionStates.length > 0) { %>
107
- // Available transitions: <%= transitionStates.join(', ') %>
108
- <% } %>
109
- // Database helpers (see src/lib/database.ts):
110
- // import { updateUserState } from '../../lib/database.js';
111
- // await updateUserState(String(context.telegramId), 'nextState', { lastMessage: text });
112
- });
113
-
114
- console.log('✅ State handler registered: <%= stateName %>');
@@ -1,53 +0,0 @@
1
- /**
2
- * Bot entry point
3
- *
4
- * This file starts the bot using the Telemeister framework.
5
- * It imports bot runners from 'telemeister/core/bot' and the database adapter
6
- * from your local database implementation.
7
- */
8
-
9
- import 'dotenv/config';
10
- import { startPollingMode, startWebhookMode } from 'telemeister/core/bot';
11
- import { databaseAdapter } from './lib/database.js';
12
- import './handlers/index.js';
13
-
14
- const botToken = process.env.BOT_TOKEN;
15
- if (!botToken) {
16
- console.error('❌ BOT_TOKEN environment variable is required');
17
- process.exit(1);
18
- }
19
-
20
- // Type assertion after validation
21
- const token: string = botToken;
22
-
23
- const botMode = process.env.BOT_MODE || 'polling';
24
-
25
- async function main(): Promise<void> {
26
- console.log(`🚀 Starting bot in ${botMode} mode...`);
27
-
28
- if (botMode === 'webhook') {
29
- const webhookUrl = process.env.WEBHOOK_URL;
30
- if (!webhookUrl) {
31
- console.error('❌ WEBHOOK_URL environment variable is required for webhook mode');
32
- process.exit(1);
33
- }
34
- const port = parseInt(process.env.PORT || '3000', 10);
35
-
36
- await startWebhookMode({
37
- token: token,
38
- database: databaseAdapter,
39
- webhookUrl,
40
- port,
41
- });
42
- } else {
43
- await startPollingMode({
44
- token: token,
45
- database: databaseAdapter,
46
- });
47
- }
48
- }
49
-
50
- main().catch((error) => {
51
- console.error('Failed to start bot:', error);
52
- process.exit(1);
53
- });
@@ -1,41 +0,0 @@
1
- {
2
- "name": "<%= botName %>",
3
- "version": "0.0.1",
4
- "type": "module",
5
- "scripts": {
6
- "dev": "tsx watch src/index.ts",
7
- "dev:webhook": "BOT_MODE=webhook tsx watch src/index.ts",
8
- "build": "tsc",
9
- "start": "node dist/index.js",
10
- "start:webhook": "BOT_MODE=webhook node dist/index.js",
11
- "db:generate": "prisma generate",
12
- "db:migrate": "prisma migrate dev",
13
- "db:deploy": "prisma migrate deploy",
14
- "db:push": "prisma db push",
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",
19
- "state:add": "telemeister state:add",
20
- "state:delete": "telemeister state:delete",
21
- "state:sync": "telemeister state:sync",
22
- "state:transition:add": "telemeister state:transition:add",
23
- "state:transition:delete": "telemeister state:transition:delete"
24
- },
25
- "dependencies": {
26
- "@prisma/adapter-better-sqlite3": "^7.0.0",
27
- "@prisma/client": "^7.4.0",
28
- "better-sqlite3": "^12.6.2",
29
- "dotenv": "^16.4.7",
30
- "express": "^4.21.2",
31
- "grammy": "^1.35.1",
32
- "telemeister": "^0.1.8"
33
- },
34
- "devDependencies": {
35
- "@types/express": "^5.0.0",
36
- "@types/node": "^20.0.0",
37
- "prisma": "^7.0.0",
38
- "tsx": "^4.0.0",
39
- "typescript": "^5.0.0"
40
- }
41
- }
@@ -1,29 +0,0 @@
1
- generator client {
2
- provider = "prisma-client-js"
3
- output = "../src/generated/prisma"
4
- }
5
-
6
- datasource db {
7
- provider = "sqlite"
8
- }
9
-
10
- model User {
11
- id Int @id @default(autoincrement())
12
- telegramId String @unique
13
- chatId String
14
- currentState String @default("idle")
15
- updatedAt DateTime @updatedAt
16
- info UserInfo?
17
-
18
- @@index([currentState])
19
- }
20
-
21
- model UserInfo {
22
- id Int @id @default(autoincrement())
23
- userId Int @unique
24
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
25
- stateData String @default("{}")
26
-
27
- // Add your custom fields below this line
28
- // Example: email String?
29
- }
@@ -1,9 +0,0 @@
1
- import 'dotenv/config';
2
- import { defineConfig, env } from 'prisma/config';
3
-
4
- export default defineConfig({
5
- schema: 'prisma/schema.prisma',
6
- datasource: {
7
- url: env('DATABASE_URL'),
8
- },
9
- });
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "esModuleInterop": true,
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "outDir": "./dist",
12
- "rootDir": "./src",
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
19
- }