@elizaos/plugin-telegram 2.0.0-alpha.1 → 2.0.0-alpha.3

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/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var __toESM = (mod, isNodeMode, target) => {
18
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
- // ../../../node_modules/telegraf/node_modules/debug/node_modules/ms/index.js
21
+ // ../../../node_modules/ms/index.js
22
22
  var require_ms = __commonJS((exports, module) => {
23
23
  var s = 1000;
24
24
  var m = s * 60;
@@ -128,7 +128,7 @@ var require_ms = __commonJS((exports, module) => {
128
128
  }
129
129
  });
130
130
 
131
- // ../../../node_modules/telegraf/node_modules/debug/src/common.js
131
+ // ../../../node_modules/debug/src/common.js
132
132
  var require_common = __commonJS((exports, module) => {
133
133
  function setup(env) {
134
134
  createDebug.debug = createDebug;
@@ -303,7 +303,7 @@ var require_common = __commonJS((exports, module) => {
303
303
  module.exports = setup;
304
304
  });
305
305
 
306
- // ../../../node_modules/telegraf/node_modules/debug/src/browser.js
306
+ // ../../../node_modules/debug/src/browser.js
307
307
  var require_browser = __commonJS((exports, module) => {
308
308
  exports.formatArgs = formatArgs;
309
309
  exports.save = save;
@@ -479,21 +479,19 @@ var require_supports_color = __commonJS((exports, module) => {
479
479
  var tty = __require("tty");
480
480
  var hasFlag = require_has_flag();
481
481
  var { env } = process;
482
- var flagForceColor;
482
+ var forceColor;
483
483
  if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
484
- flagForceColor = 0;
484
+ forceColor = 0;
485
485
  } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
486
- flagForceColor = 1;
486
+ forceColor = 1;
487
487
  }
488
- function envForceColor() {
489
- if ("FORCE_COLOR" in env) {
490
- if (env.FORCE_COLOR === "true") {
491
- return 1;
492
- }
493
- if (env.FORCE_COLOR === "false") {
494
- return 0;
495
- }
496
- return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
488
+ if ("FORCE_COLOR" in env) {
489
+ if (env.FORCE_COLOR === "true") {
490
+ forceColor = 1;
491
+ } else if (env.FORCE_COLOR === "false") {
492
+ forceColor = 0;
493
+ } else {
494
+ forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
497
495
  }
498
496
  }
499
497
  function translateLevel(level) {
@@ -507,22 +505,15 @@ var require_supports_color = __commonJS((exports, module) => {
507
505
  has16m: level >= 3
508
506
  };
509
507
  }
510
- function supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
511
- const noFlagForceColor = envForceColor();
512
- if (noFlagForceColor !== undefined) {
513
- flagForceColor = noFlagForceColor;
514
- }
515
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
508
+ function supportsColor(haveStream, streamIsTTY) {
516
509
  if (forceColor === 0) {
517
510
  return 0;
518
511
  }
519
- if (sniffFlags) {
520
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
521
- return 3;
522
- }
523
- if (hasFlag("color=256")) {
524
- return 2;
525
- }
512
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
513
+ return 3;
514
+ }
515
+ if (hasFlag("color=256")) {
516
+ return 2;
526
517
  }
527
518
  if (haveStream && !streamIsTTY && forceColor === undefined) {
528
519
  return 0;
@@ -539,7 +530,7 @@ var require_supports_color = __commonJS((exports, module) => {
539
530
  return 1;
540
531
  }
541
532
  if ("CI" in env) {
542
- if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
533
+ if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
543
534
  return 1;
544
535
  }
545
536
  return min;
@@ -551,7 +542,7 @@ var require_supports_color = __commonJS((exports, module) => {
551
542
  return 3;
552
543
  }
553
544
  if ("TERM_PROGRAM" in env) {
554
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
545
+ const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
555
546
  switch (env.TERM_PROGRAM) {
556
547
  case "iTerm.app":
557
548
  return version >= 3 ? 3 : 2;
@@ -570,21 +561,18 @@ var require_supports_color = __commonJS((exports, module) => {
570
561
  }
571
562
  return min;
572
563
  }
573
- function getSupportLevel(stream, options = {}) {
574
- const level = supportsColor(stream, {
575
- streamIsTTY: stream && stream.isTTY,
576
- ...options
577
- });
564
+ function getSupportLevel(stream) {
565
+ const level = supportsColor(stream, stream && stream.isTTY);
578
566
  return translateLevel(level);
579
567
  }
580
568
  module.exports = {
581
569
  supportsColor: getSupportLevel,
582
- stdout: getSupportLevel({ isTTY: tty.isatty(1) }),
583
- stderr: getSupportLevel({ isTTY: tty.isatty(2) })
570
+ stdout: translateLevel(supportsColor(true, tty.isatty(1))),
571
+ stderr: translateLevel(supportsColor(true, tty.isatty(2)))
584
572
  };
585
573
  });
586
574
 
587
- // ../../../node_modules/telegraf/node_modules/debug/src/node.js
575
+ // ../../../node_modules/debug/src/node.js
588
576
  var require_node = __commonJS((exports, module) => {
589
577
  var tty = __require("tty");
590
578
  var util = __require("util");
@@ -755,7 +743,7 @@ var require_node = __commonJS((exports, module) => {
755
743
  };
756
744
  });
757
745
 
758
- // ../../../node_modules/telegraf/node_modules/debug/src/index.js
746
+ // ../../../node_modules/debug/src/index.js
759
747
  var require_src = __commonJS((exports, module) => {
760
748
  if (typeof process === "undefined" || process.type === "renderer" || false || process.__nwjs) {
761
749
  module.exports = require_browser();
@@ -2765,7 +2753,7 @@ var require_abort_controller = __commonJS((exports, module) => {
2765
2753
  });
2766
2754
  }
2767
2755
 
2768
- class AbortController {
2756
+ class AbortController2 {
2769
2757
  constructor() {
2770
2758
  signals.set(this, createAbortSignal());
2771
2759
  }
@@ -2784,21 +2772,21 @@ var require_abort_controller = __commonJS((exports, module) => {
2784
2772
  }
2785
2773
  return signal;
2786
2774
  }
2787
- Object.defineProperties(AbortController.prototype, {
2775
+ Object.defineProperties(AbortController2.prototype, {
2788
2776
  signal: { enumerable: true },
2789
2777
  abort: { enumerable: true }
2790
2778
  });
2791
2779
  if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
2792
- Object.defineProperty(AbortController.prototype, Symbol.toStringTag, {
2780
+ Object.defineProperty(AbortController2.prototype, Symbol.toStringTag, {
2793
2781
  configurable: true,
2794
2782
  value: "AbortController"
2795
2783
  });
2796
2784
  }
2797
- exports.AbortController = AbortController;
2785
+ exports.AbortController = AbortController2;
2798
2786
  exports.AbortSignal = AbortSignal;
2799
- exports.default = AbortController;
2800
- module.exports = AbortController;
2801
- module.exports.AbortController = module.exports["default"] = AbortController;
2787
+ exports.default = AbortController2;
2788
+ module.exports = AbortController2;
2789
+ module.exports.AbortController = module.exports["default"] = AbortController2;
2802
2790
  module.exports.AbortSignal = AbortSignal;
2803
2791
  });
2804
2792
 
@@ -9150,12 +9138,12 @@ var require_lib3 = __commonJS((exports, module) => {
9150
9138
  AbortError.prototype.name = "AbortError";
9151
9139
  var URL$1 = Url.URL || whatwgUrl.URL;
9152
9140
  var PassThrough$1 = Stream.PassThrough;
9153
- var isDomainOrSubdomain = function isDomainOrSubdomain(destination, original) {
9141
+ var isDomainOrSubdomain = function isDomainOrSubdomain2(destination, original) {
9154
9142
  const orig = new URL$1(original).hostname;
9155
9143
  const dest = new URL$1(destination).hostname;
9156
9144
  return orig === dest || orig[orig.length - dest.length - 1] === "." && orig.endsWith(dest);
9157
9145
  };
9158
- var isSameProtocol = function isSameProtocol(destination, original) {
9146
+ var isSameProtocol = function isSameProtocol2(destination, original) {
9159
9147
  const orig = new URL$1(original).protocol;
9160
9148
  const dest = new URL$1(destination).protocol;
9161
9149
  return orig === dest;
@@ -9171,7 +9159,7 @@ var require_lib3 = __commonJS((exports, module) => {
9171
9159
  const send = (options.protocol === "https:" ? https : http).request;
9172
9160
  const signal = request.signal;
9173
9161
  let response = null;
9174
- const abort = function abort() {
9162
+ const abort = function abort2() {
9175
9163
  let error = new AbortError("The user aborted a request.");
9176
9164
  reject(error);
9177
9165
  if (request.body && request.body instanceof Stream.Readable) {
@@ -9185,7 +9173,7 @@ var require_lib3 = __commonJS((exports, module) => {
9185
9173
  abort();
9186
9174
  return;
9187
9175
  }
9188
- const abortAndFinalize = function abortAndFinalize() {
9176
+ const abortAndFinalize = function abortAndFinalize2() {
9189
9177
  abort();
9190
9178
  finalize();
9191
9179
  };
@@ -10866,7 +10854,7 @@ var require_buffer_alloc = __commonJS((exports, module) => {
10866
10854
  var require_safe_compare = __commonJS((exports, module) => {
10867
10855
  var crypto = __require("crypto");
10868
10856
  var bufferAlloc = require_buffer_alloc();
10869
- var safeCompare = function safeCompare(a, b) {
10857
+ var safeCompare = function safeCompare2(a, b) {
10870
10858
  var strA = String(a);
10871
10859
  var strB = String(b);
10872
10860
  var lenA = strA.length;
@@ -10880,7 +10868,7 @@ var require_safe_compare = __commonJS((exports, module) => {
10880
10868
  }
10881
10869
  return result === 0;
10882
10870
  };
10883
- var nativeTimingSafeEqual = function nativeTimingSafeEqual(a, b) {
10871
+ var nativeTimingSafeEqual = function nativeTimingSafeEqual2(a, b) {
10884
10872
  var strA = String(a);
10885
10873
  var strB = String(b);
10886
10874
  var aLen = Buffer.byteLength(strA);
@@ -12034,6 +12022,321 @@ var sendMessageAction = {
12034
12022
  ]
12035
12023
  ]
12036
12024
  };
12025
+ // src/actions/sendReaction.ts
12026
+ import { composePromptFromState, ModelType, parseJSONObjectFromText } from "@elizaos/core";
12027
+
12028
+ // src/types.ts
12029
+ var TelegramEventTypes;
12030
+ ((TelegramEventTypes2) => {
12031
+ TelegramEventTypes2["WORLD_JOINED"] = "TELEGRAM_WORLD_JOINED";
12032
+ TelegramEventTypes2["WORLD_CONNECTED"] = "TELEGRAM_WORLD_CONNECTED";
12033
+ TelegramEventTypes2["WORLD_LEFT"] = "TELEGRAM_WORLD_LEFT";
12034
+ TelegramEventTypes2["ENTITY_JOINED"] = "TELEGRAM_ENTITY_JOINED";
12035
+ TelegramEventTypes2["ENTITY_LEFT"] = "TELEGRAM_ENTITY_LEFT";
12036
+ TelegramEventTypes2["ENTITY_UPDATED"] = "TELEGRAM_ENTITY_UPDATED";
12037
+ TelegramEventTypes2["MESSAGE_RECEIVED"] = "TELEGRAM_MESSAGE_RECEIVED";
12038
+ TelegramEventTypes2["MESSAGE_SENT"] = "TELEGRAM_MESSAGE_SENT";
12039
+ TelegramEventTypes2["REACTION_RECEIVED"] = "TELEGRAM_REACTION_RECEIVED";
12040
+ TelegramEventTypes2["REACTION_SENT"] = "TELEGRAM_REACTION_SENT";
12041
+ TelegramEventTypes2["INTERACTION_RECEIVED"] = "TELEGRAM_INTERACTION_RECEIVED";
12042
+ TelegramEventTypes2["SLASH_START"] = "TELEGRAM_SLASH_START";
12043
+ TelegramEventTypes2["BOT_STARTED"] = "TELEGRAM_BOT_STARTED";
12044
+ TelegramEventTypes2["BOT_STOPPED"] = "TELEGRAM_BOT_STOPPED";
12045
+ TelegramEventTypes2["WEBHOOK_REGISTERED"] = "TELEGRAM_WEBHOOK_REGISTERED";
12046
+ })(TelegramEventTypes ||= {});
12047
+ var TELEGRAM_REACTIONS = {
12048
+ THUMBS_UP: "\uD83D\uDC4D",
12049
+ THUMBS_DOWN: "\uD83D\uDC4E",
12050
+ HEART: "❤",
12051
+ FIRE: "\uD83D\uDD25",
12052
+ CELEBRATION: "\uD83C\uDF89",
12053
+ CRYING: "\uD83D\uDE22",
12054
+ THINKING: "\uD83E\uDD14",
12055
+ EXPLODING_HEAD: "\uD83E\uDD2F",
12056
+ SCREAMING: "\uD83D\uDE31",
12057
+ ANGRY: "\uD83E\uDD2C",
12058
+ SKULL: "\uD83D\uDC80",
12059
+ POOP: "\uD83D\uDCA9",
12060
+ CLOWN: "\uD83E\uDD21",
12061
+ QUESTION: "\uD83E\uDD28",
12062
+ EYES: "\uD83D\uDC40",
12063
+ WHALE: "\uD83D\uDC33",
12064
+ HEART_ON_FIRE: "❤️‍\uD83D\uDD25",
12065
+ NEW_MOON: "\uD83C\uDF1A",
12066
+ HOT_DOG: "\uD83C\uDF2D",
12067
+ HUNDRED: "\uD83D\uDCAF",
12068
+ TEARS_OF_JOY: "\uD83D\uDE02",
12069
+ LIGHTNING: "⚡",
12070
+ BANANA: "\uD83C\uDF4C",
12071
+ TROPHY: "\uD83C\uDFC6",
12072
+ BROKEN_HEART: "\uD83D\uDC94",
12073
+ FACE_WITH_RAISED_EYEBROW: "\uD83E\uDD28",
12074
+ NEUTRAL: "\uD83D\uDE10",
12075
+ STRAWBERRY: "\uD83C\uDF53",
12076
+ CHAMPAGNE: "\uD83C\uDF7E",
12077
+ KISS: "\uD83D\uDC8B",
12078
+ MIDDLE_FINGER: "\uD83D\uDD95",
12079
+ DEVIL: "\uD83D\uDE08",
12080
+ SLEEPING: "\uD83D\uDE34",
12081
+ LOUDLY_CRYING: "\uD83D\uDE2D",
12082
+ NERD: "\uD83E\uDD13",
12083
+ GHOST: "\uD83D\uDC7B",
12084
+ TECHNOLOGIST: "\uD83D\uDC68‍\uD83D\uDCBB",
12085
+ UNICORN: "\uD83E\uDD84"
12086
+ };
12087
+
12088
+ // src/actions/sendReaction.ts
12089
+ var SEND_REACTION_ACTION = "SEND_TELEGRAM_REACTION";
12090
+ var REACTION_EXTRACTION_TEMPLATE = `
12091
+ You are extracting reaction parameters from a conversation.
12092
+
12093
+ The user wants to react to a Telegram message. Extract the following:
12094
+ 1. reaction: The emoji to use as a reaction (must be a valid Telegram reaction emoji)
12095
+ 2. messageId: The message ID to react to (if specified, otherwise use the current message)
12096
+ 3. isBig: Whether to use a big/animated reaction (default: false)
12097
+
12098
+ Valid Telegram reaction emojis include:
12099
+ \uD83D\uDC4D \uD83D\uDC4E ❤ \uD83D\uDD25 \uD83C\uDF89 \uD83D\uDE22 \uD83E\uDD14 \uD83E\uDD2F \uD83D\uDE31 \uD83E\uDD2C \uD83D\uDC80 \uD83D\uDCA9 \uD83E\uDD21 \uD83E\uDD28 \uD83D\uDC40 \uD83D\uDC33 ❤️‍\uD83D\uDD25 \uD83C\uDF1A \uD83C\uDF2D \uD83D\uDCAF \uD83D\uDE02 ⚡ \uD83C\uDF4C \uD83C\uDFC6 \uD83D\uDC94 \uD83D\uDE10 \uD83C\uDF53 \uD83C\uDF7E \uD83D\uDC8B \uD83D\uDD95 \uD83D\uDE08 \uD83D\uDE34 \uD83D\uDE2D \uD83E\uDD13 \uD83D\uDC7B \uD83D\uDC68‍\uD83D\uDCBB \uD83E\uDD84
12100
+
12101
+ {{recentMessages}}
12102
+
12103
+ Based on the conversation, extract the reaction parameters.
12104
+ If the user mentions "thumbs up", "like", or similar, use \uD83D\uDC4D.
12105
+ If the user mentions "heart", "love", use ❤.
12106
+ If the user mentions "fire", "lit", use \uD83D\uDD25.
12107
+ If the user doesn't specify a message, react to their current message.
12108
+
12109
+ Respond with a JSON object:
12110
+ {
12111
+ "reaction": "emoji here",
12112
+ "messageId": number or null,
12113
+ "isBig": boolean
12114
+ }
12115
+ `;
12116
+ var sendReactionAction = {
12117
+ name: SEND_REACTION_ACTION,
12118
+ similes: [
12119
+ "TELEGRAM_REACT",
12120
+ "TELEGRAM_REACTION",
12121
+ "REACT_TO_MESSAGE",
12122
+ "ADD_REACTION",
12123
+ "SEND_EMOJI",
12124
+ "TELEGRAM_EMOJI"
12125
+ ],
12126
+ description: "Send a reaction emoji to a Telegram message",
12127
+ validate: async (_runtime, message) => {
12128
+ const source = message.content?.source;
12129
+ return source === "telegram";
12130
+ },
12131
+ handler: async (runtime, message, state, _options, callback) => {
12132
+ const telegramService = runtime.getService(TELEGRAM_SERVICE_NAME);
12133
+ if (!telegramService) {
12134
+ if (callback) {
12135
+ await callback({
12136
+ text: "Telegram service not available"
12137
+ });
12138
+ }
12139
+ return { success: false, error: "Telegram service not initialized" };
12140
+ }
12141
+ const chatId = message.content?.chatId;
12142
+ if (!chatId) {
12143
+ if (callback) {
12144
+ await callback({
12145
+ text: "No chat ID available"
12146
+ });
12147
+ }
12148
+ return { success: false, error: "Missing chat ID" };
12149
+ }
12150
+ const currentState = state ?? await runtime.composeState(message);
12151
+ const prompt = composePromptFromState({
12152
+ state: currentState,
12153
+ template: REACTION_EXTRACTION_TEMPLATE
12154
+ });
12155
+ let params;
12156
+ try {
12157
+ const response = await runtime.useModel(ModelType.TEXT_SMALL, {
12158
+ prompt
12159
+ });
12160
+ const parsed = parseJSONObjectFromText(response);
12161
+ if (!parsed || !parsed.reaction) {
12162
+ params = {
12163
+ reaction: TELEGRAM_REACTIONS.THUMBS_UP,
12164
+ isBig: false
12165
+ };
12166
+ } else {
12167
+ params = parsed;
12168
+ }
12169
+ } catch {
12170
+ params = {
12171
+ reaction: TELEGRAM_REACTIONS.THUMBS_UP,
12172
+ isBig: false
12173
+ };
12174
+ }
12175
+ const targetMessageId = params.messageId ?? message.content?.messageId;
12176
+ if (!targetMessageId) {
12177
+ if (callback) {
12178
+ await callback({
12179
+ text: "No message ID available to react to"
12180
+ });
12181
+ }
12182
+ return { success: false, error: "Missing message ID" };
12183
+ }
12184
+ const result = await telegramService.sendReaction({
12185
+ chatId,
12186
+ messageId: targetMessageId,
12187
+ reaction: params.reaction,
12188
+ isBig: params.isBig
12189
+ });
12190
+ if (result.success) {
12191
+ if (callback) {
12192
+ await callback({
12193
+ text: `Reacted with ${params.reaction}`,
12194
+ action: SEND_REACTION_ACTION
12195
+ });
12196
+ }
12197
+ return {
12198
+ success: true,
12199
+ data: {
12200
+ action: SEND_REACTION_ACTION,
12201
+ chatId,
12202
+ messageId: targetMessageId,
12203
+ reaction: params.reaction
12204
+ }
12205
+ };
12206
+ } else {
12207
+ if (callback) {
12208
+ await callback({
12209
+ text: `Failed to send reaction: ${result.error}`
12210
+ });
12211
+ }
12212
+ return { success: false, error: result.error };
12213
+ }
12214
+ },
12215
+ examples: [
12216
+ [
12217
+ {
12218
+ name: "{{name1}}",
12219
+ content: {
12220
+ text: "React to my message with a thumbs up"
12221
+ }
12222
+ },
12223
+ {
12224
+ name: "{{agentName}}",
12225
+ content: {
12226
+ text: "I'll add a thumbs up reaction to your message.",
12227
+ actions: [SEND_REACTION_ACTION]
12228
+ }
12229
+ }
12230
+ ],
12231
+ [
12232
+ {
12233
+ name: "{{name1}}",
12234
+ content: {
12235
+ text: "Give that a heart reaction"
12236
+ }
12237
+ },
12238
+ {
12239
+ name: "{{agentName}}",
12240
+ content: {
12241
+ text: "Adding a heart reaction now.",
12242
+ actions: [SEND_REACTION_ACTION]
12243
+ }
12244
+ }
12245
+ ],
12246
+ [
12247
+ {
12248
+ name: "{{name1}}",
12249
+ content: {
12250
+ text: "\uD83D\uDD25 that message"
12251
+ }
12252
+ },
12253
+ {
12254
+ name: "{{agentName}}",
12255
+ content: {
12256
+ text: "I'll react with the fire emoji.",
12257
+ actions: [SEND_REACTION_ACTION]
12258
+ }
12259
+ }
12260
+ ]
12261
+ ]
12262
+ };
12263
+ // src/environment.ts
12264
+ import { z } from "zod";
12265
+ var telegramEnvSchema = z.object({
12266
+ TELEGRAM_BOT_TOKEN: z.string().min(1, "Telegram bot token is required"),
12267
+ TELEGRAM_API_ROOT: z.string().optional(),
12268
+ TELEGRAM_UPDATE_MODE: z.enum(["polling", "webhook"]).optional().default("polling"),
12269
+ TELEGRAM_WEBHOOK_URL: z.string().url().optional(),
12270
+ TELEGRAM_WEBHOOK_PATH: z.string().optional(),
12271
+ TELEGRAM_WEBHOOK_PORT: z.coerce.number().optional(),
12272
+ TELEGRAM_WEBHOOK_SECRET: z.string().optional(),
12273
+ TELEGRAM_ALLOWED_CHATS: z.string().optional(),
12274
+ TELEGRAM_PROXY_URL: z.string().url().optional(),
12275
+ TELEGRAM_DROP_PENDING_UPDATES: z.coerce.boolean().optional().default(true),
12276
+ TELEGRAM_SHOULD_IGNORE_BOT_MESSAGES: z.coerce.boolean().optional().default(true),
12277
+ TELEGRAM_SHOULD_RESPOND_ONLY_TO_MENTIONS: z.coerce.boolean().optional().default(false)
12278
+ });
12279
+ function parseAllowedChats(value) {
12280
+ if (!value)
12281
+ return [];
12282
+ const trimmed = value.trim();
12283
+ if (!trimmed)
12284
+ return [];
12285
+ if (trimmed.startsWith("[")) {
12286
+ const parsed = JSON.parse(trimmed);
12287
+ if (Array.isArray(parsed)) {
12288
+ return parsed.map((id) => {
12289
+ const num = typeof id === "string" ? parseInt(id, 10) : Number(id);
12290
+ return Number.isFinite(num) ? num : 0;
12291
+ }).filter((id) => id !== 0);
12292
+ }
12293
+ }
12294
+ return trimmed.split(",").map((s) => parseInt(s.trim(), 10)).filter((id) => Number.isFinite(id));
12295
+ }
12296
+ async function validateTelegramConfig(runtime) {
12297
+ try {
12298
+ const config = {
12299
+ TELEGRAM_BOT_TOKEN: runtime.getSetting("TELEGRAM_BOT_TOKEN") || process.env.TELEGRAM_BOT_TOKEN,
12300
+ TELEGRAM_API_ROOT: runtime.getSetting("TELEGRAM_API_ROOT") || process.env.TELEGRAM_API_ROOT,
12301
+ TELEGRAM_UPDATE_MODE: runtime.getSetting("TELEGRAM_UPDATE_MODE") || process.env.TELEGRAM_UPDATE_MODE,
12302
+ TELEGRAM_WEBHOOK_URL: runtime.getSetting("TELEGRAM_WEBHOOK_URL") || process.env.TELEGRAM_WEBHOOK_URL,
12303
+ TELEGRAM_WEBHOOK_PATH: runtime.getSetting("TELEGRAM_WEBHOOK_PATH") || process.env.TELEGRAM_WEBHOOK_PATH,
12304
+ TELEGRAM_WEBHOOK_PORT: runtime.getSetting("TELEGRAM_WEBHOOK_PORT") || process.env.TELEGRAM_WEBHOOK_PORT,
12305
+ TELEGRAM_WEBHOOK_SECRET: runtime.getSetting("TELEGRAM_WEBHOOK_SECRET") || process.env.TELEGRAM_WEBHOOK_SECRET,
12306
+ TELEGRAM_ALLOWED_CHATS: runtime.getSetting("TELEGRAM_ALLOWED_CHATS") || process.env.TELEGRAM_ALLOWED_CHATS,
12307
+ TELEGRAM_PROXY_URL: runtime.getSetting("TELEGRAM_PROXY_URL") || process.env.TELEGRAM_PROXY_URL,
12308
+ TELEGRAM_DROP_PENDING_UPDATES: runtime.getSetting("TELEGRAM_DROP_PENDING_UPDATES") || process.env.TELEGRAM_DROP_PENDING_UPDATES,
12309
+ TELEGRAM_SHOULD_IGNORE_BOT_MESSAGES: runtime.getSetting("TELEGRAM_SHOULD_IGNORE_BOT_MESSAGES") || process.env.TELEGRAM_SHOULD_IGNORE_BOT_MESSAGES,
12310
+ TELEGRAM_SHOULD_RESPOND_ONLY_TO_MENTIONS: runtime.getSetting("TELEGRAM_SHOULD_RESPOND_ONLY_TO_MENTIONS") || process.env.TELEGRAM_SHOULD_RESPOND_ONLY_TO_MENTIONS
12311
+ };
12312
+ return telegramEnvSchema.parse(config);
12313
+ } catch (error) {
12314
+ if (error instanceof z.ZodError) {
12315
+ const errorMessages = error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(`
12316
+ `);
12317
+ console.warn(`Telegram configuration validation failed:
12318
+ ${errorMessages}`);
12319
+ }
12320
+ return null;
12321
+ }
12322
+ }
12323
+ function buildTelegramSettings(config) {
12324
+ return {
12325
+ botToken: config.TELEGRAM_BOT_TOKEN,
12326
+ apiRoot: config.TELEGRAM_API_ROOT || "https://api.telegram.org",
12327
+ updateMode: config.TELEGRAM_UPDATE_MODE || "polling",
12328
+ webhookUrl: config.TELEGRAM_WEBHOOK_URL,
12329
+ webhookPath: config.TELEGRAM_WEBHOOK_PATH || "/telegram/webhook",
12330
+ webhookPort: config.TELEGRAM_WEBHOOK_PORT,
12331
+ webhookSecret: config.TELEGRAM_WEBHOOK_SECRET,
12332
+ allowedChatIds: parseAllowedChats(config.TELEGRAM_ALLOWED_CHATS),
12333
+ proxyUrl: config.TELEGRAM_PROXY_URL,
12334
+ dropPendingUpdates: config.TELEGRAM_DROP_PENDING_UPDATES ?? true,
12335
+ shouldIgnoreBotMessages: config.TELEGRAM_SHOULD_IGNORE_BOT_MESSAGES ?? true,
12336
+ shouldRespondOnlyToMentions: config.TELEGRAM_SHOULD_RESPOND_ONLY_TO_MENTIONS ?? false
12337
+ };
12338
+ }
12339
+
12037
12340
  // src/messageManager.ts
12038
12341
  var import_telegraf2 = __toESM(require_lib4(), 1);
12039
12342
  import fs from "node:fs";
@@ -12043,7 +12346,7 @@ import {
12043
12346
  EventType,
12044
12347
  logger as logger2,
12045
12348
  MemoryType,
12046
- ModelType,
12349
+ ModelType as ModelType2,
12047
12350
  ServiceType
12048
12351
  } from "@elizaos/core";
12049
12352
 
@@ -12212,7 +12515,7 @@ class MessageManager {
12212
12515
  imageUrl = fileLink.toString();
12213
12516
  }
12214
12517
  if (imageUrl) {
12215
- const { title, description } = await this.runtime.useModel(ModelType.IMAGE_DESCRIPTION, imageUrl);
12518
+ const { title, description } = await this.runtime.useModel(ModelType2.IMAGE_DESCRIPTION, imageUrl);
12216
12519
  return { description: `[Image: ${title}
12217
12520
  ${description}]` };
12218
12521
  }
@@ -12810,12 +13113,134 @@ var chatStateProvider = {
12810
13113
  var import_telegraf3 = __toESM(require_lib4(), 1);
12811
13114
  import {
12812
13115
  ChannelType as ChannelType2,
13116
+ checkPairingAllowed,
12813
13117
  createUniqueUuid as createUniqueUuid2,
12814
13118
  EventType as EventType2,
13119
+ isInAllowlist,
12815
13120
  logger as logger3,
12816
13121
  Role,
12817
13122
  Service
12818
13123
  } from "@elizaos/core";
13124
+
13125
+ // src/accounts.ts
13126
+ var DEFAULT_ACCOUNT_ID = "default";
13127
+ function normalizeAccountId(accountId) {
13128
+ if (!accountId || typeof accountId !== "string") {
13129
+ return DEFAULT_ACCOUNT_ID;
13130
+ }
13131
+ const trimmed = accountId.trim().toLowerCase();
13132
+ return trimmed || DEFAULT_ACCOUNT_ID;
13133
+ }
13134
+ function normalizeTelegramToken(raw) {
13135
+ const trimmed = raw?.trim();
13136
+ return trimmed || undefined;
13137
+ }
13138
+ function getMultiAccountConfig(runtime) {
13139
+ const characterTelegram = runtime.character?.settings?.telegram;
13140
+ return {
13141
+ enabled: characterTelegram?.enabled,
13142
+ token: characterTelegram?.token,
13143
+ accounts: characterTelegram?.accounts
13144
+ };
13145
+ }
13146
+ function listTelegramAccountIds(runtime) {
13147
+ const config = getMultiAccountConfig(runtime);
13148
+ const accounts = config.accounts;
13149
+ if (!accounts || typeof accounts !== "object") {
13150
+ return [DEFAULT_ACCOUNT_ID];
13151
+ }
13152
+ const ids = Object.keys(accounts).filter(Boolean);
13153
+ if (ids.length === 0) {
13154
+ return [DEFAULT_ACCOUNT_ID];
13155
+ }
13156
+ return ids.slice().sort((a, b) => a.localeCompare(b));
13157
+ }
13158
+ function resolveDefaultTelegramAccountId(runtime) {
13159
+ const ids = listTelegramAccountIds(runtime);
13160
+ if (ids.includes(DEFAULT_ACCOUNT_ID)) {
13161
+ return DEFAULT_ACCOUNT_ID;
13162
+ }
13163
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
13164
+ }
13165
+ function getAccountConfig(runtime, accountId) {
13166
+ const config = getMultiAccountConfig(runtime);
13167
+ const accounts = config.accounts;
13168
+ if (!accounts || typeof accounts !== "object") {
13169
+ return;
13170
+ }
13171
+ return accounts[accountId];
13172
+ }
13173
+ function mergeTelegramAccountConfig(runtime, accountId) {
13174
+ const multiConfig = getMultiAccountConfig(runtime);
13175
+ const { accounts: _ignored, ...baseConfig } = multiConfig;
13176
+ const accountConfig = getAccountConfig(runtime, accountId) ?? {};
13177
+ const envUpdateMode = runtime.getSetting("TELEGRAM_UPDATE_MODE");
13178
+ const envWebhookUrl = runtime.getSetting("TELEGRAM_WEBHOOK_URL");
13179
+ const envWebhookPort = runtime.getSetting("TELEGRAM_WEBHOOK_PORT");
13180
+ const envWebhookPath = runtime.getSetting("TELEGRAM_WEBHOOK_PATH");
13181
+ const envWebhookSecret = runtime.getSetting("TELEGRAM_WEBHOOK_SECRET");
13182
+ const envApiRoot = runtime.getSetting("TELEGRAM_API_ROOT");
13183
+ const envAllowedChats = runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
13184
+ const envConfig = {
13185
+ updateMode: envUpdateMode === "webhook" ? "webhook" : "polling",
13186
+ webhookUrl: envWebhookUrl || undefined,
13187
+ webhookPort: envWebhookPort ? parseInt(envWebhookPort, 10) : undefined,
13188
+ webhookPath: envWebhookPath || undefined,
13189
+ webhookSecret: envWebhookSecret || undefined,
13190
+ apiRoot: envApiRoot || undefined,
13191
+ allowedChatIds: envAllowedChats ? envAllowedChats.split(",").map((s) => s.trim()).filter(Boolean) : undefined
13192
+ };
13193
+ return {
13194
+ ...envConfig,
13195
+ ...baseConfig,
13196
+ ...accountConfig
13197
+ };
13198
+ }
13199
+ function resolveTelegramToken(runtime, opts = {}) {
13200
+ const accountId = normalizeAccountId(opts.accountId);
13201
+ const multiConfig = getMultiAccountConfig(runtime);
13202
+ const accountConfig = accountId !== DEFAULT_ACCOUNT_ID ? multiConfig.accounts?.[accountId] : multiConfig.accounts?.[DEFAULT_ACCOUNT_ID];
13203
+ const accountToken = normalizeTelegramToken(accountConfig?.token);
13204
+ if (accountToken) {
13205
+ return { token: accountToken, source: "config" };
13206
+ }
13207
+ const allowBase = accountId === DEFAULT_ACCOUNT_ID;
13208
+ const baseToken = allowBase ? normalizeTelegramToken(multiConfig.token) : undefined;
13209
+ if (baseToken) {
13210
+ return { token: baseToken, source: "character" };
13211
+ }
13212
+ const envToken = allowBase ? normalizeTelegramToken(runtime.getSetting("TELEGRAM_BOT_TOKEN")) : undefined;
13213
+ if (envToken) {
13214
+ return { token: envToken, source: "env" };
13215
+ }
13216
+ return { token: "", source: "none" };
13217
+ }
13218
+ function resolveTelegramAccount(runtime, accountId) {
13219
+ const normalizedAccountId = normalizeAccountId(accountId);
13220
+ const multiConfig = getMultiAccountConfig(runtime);
13221
+ const baseEnabled = multiConfig.enabled !== false;
13222
+ const merged = mergeTelegramAccountConfig(runtime, normalizedAccountId);
13223
+ const accountEnabled = merged.enabled !== false;
13224
+ const enabled = baseEnabled && accountEnabled;
13225
+ const tokenResolution = resolveTelegramToken(runtime, { accountId: normalizedAccountId });
13226
+ return {
13227
+ accountId: normalizedAccountId,
13228
+ enabled,
13229
+ name: merged.name?.trim() || undefined,
13230
+ token: tokenResolution.token,
13231
+ tokenSource: tokenResolution.source,
13232
+ config: merged
13233
+ };
13234
+ }
13235
+ function listEnabledTelegramAccounts(runtime) {
13236
+ return listTelegramAccountIds(runtime).map((accountId) => resolveTelegramAccount(runtime, accountId)).filter((account) => account.enabled && account.token);
13237
+ }
13238
+ function isMultiAccountEnabled(runtime) {
13239
+ const accounts = listEnabledTelegramAccounts(runtime);
13240
+ return accounts.length > 1;
13241
+ }
13242
+
13243
+ // src/service.ts
12819
13244
  class TelegramService extends Service {
12820
13245
  static serviceType = TELEGRAM_SERVICE_NAME;
12821
13246
  capabilityDescription = "The agent is able to send and receive messages on telegram";
@@ -12824,6 +13249,9 @@ class TelegramService extends Service {
12824
13249
  options;
12825
13250
  knownChats = new Map;
12826
13251
  syncedEntityIds = new Set;
13252
+ settings = null;
13253
+ botInfo = null;
13254
+ webhookServer = null;
12827
13255
  constructor(runtime) {
12828
13256
  super(runtime);
12829
13257
  const botToken = runtime.getSetting("TELEGRAM_BOT_TOKEN");
@@ -12841,22 +13269,159 @@ class TelegramService extends Service {
12841
13269
  this.bot = new import_telegraf3.Telegraf(botToken, this.options);
12842
13270
  this.messageManager = new MessageManager(this.bot, this.runtime);
12843
13271
  }
13272
+ getBotInfo() {
13273
+ return this.botInfo;
13274
+ }
13275
+ getSettings() {
13276
+ return this.settings;
13277
+ }
13278
+ async probeTelegram(timeoutMs = 5000) {
13279
+ if (!this.bot) {
13280
+ return {
13281
+ ok: false,
13282
+ error: "Bot not initialized",
13283
+ latencyMs: 0
13284
+ };
13285
+ }
13286
+ const startTime = Date.now();
13287
+ const controller = new AbortController;
13288
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
13289
+ try {
13290
+ const me = await this.bot.telegram.getMe();
13291
+ const latencyMs = Date.now() - startTime;
13292
+ return {
13293
+ ok: true,
13294
+ bot: {
13295
+ id: me.id,
13296
+ username: me.username,
13297
+ firstName: me.first_name,
13298
+ canJoinGroups: me.can_join_groups ?? false,
13299
+ canReadAllGroupMessages: me.can_read_all_group_messages ?? false,
13300
+ supportsInlineQueries: me.supports_inline_queries ?? false
13301
+ },
13302
+ latencyMs
13303
+ };
13304
+ } catch (error) {
13305
+ const latencyMs = Date.now() - startTime;
13306
+ return {
13307
+ ok: false,
13308
+ error: error instanceof Error ? error.message : String(error),
13309
+ latencyMs
13310
+ };
13311
+ } finally {
13312
+ clearTimeout(timeoutId);
13313
+ }
13314
+ }
13315
+ async sendReaction(params) {
13316
+ if (!this.bot) {
13317
+ return {
13318
+ success: false,
13319
+ chatId: params.chatId,
13320
+ messageId: params.messageId,
13321
+ reaction: params.reaction,
13322
+ error: "Bot not initialized"
13323
+ };
13324
+ }
13325
+ try {
13326
+ const chatId = typeof params.chatId === "string" ? parseInt(params.chatId, 10) : params.chatId;
13327
+ const reactionType = {
13328
+ type: "emoji",
13329
+ emoji: params.reaction
13330
+ };
13331
+ await this.bot.telegram.setMessageReaction(chatId, params.messageId, [reactionType], params.isBig ?? false);
13332
+ this.runtime.emitEvent("TELEGRAM_REACTION_SENT" /* REACTION_SENT */, {
13333
+ runtime: this.runtime,
13334
+ chatId: params.chatId,
13335
+ messageId: params.messageId,
13336
+ reaction: params.reaction,
13337
+ success: true
13338
+ });
13339
+ return {
13340
+ success: true,
13341
+ chatId: params.chatId,
13342
+ messageId: params.messageId,
13343
+ reaction: params.reaction
13344
+ };
13345
+ } catch (error) {
13346
+ const errorMessage = error instanceof Error ? error.message : String(error);
13347
+ logger3.error({ error }, `Failed to send reaction: ${errorMessage}`);
13348
+ return {
13349
+ success: false,
13350
+ chatId: params.chatId,
13351
+ messageId: params.messageId,
13352
+ reaction: params.reaction,
13353
+ error: errorMessage
13354
+ };
13355
+ }
13356
+ }
13357
+ async removeReaction(chatId, messageId) {
13358
+ if (!this.bot) {
13359
+ return {
13360
+ success: false,
13361
+ chatId,
13362
+ messageId,
13363
+ reaction: "",
13364
+ error: "Bot not initialized"
13365
+ };
13366
+ }
13367
+ try {
13368
+ const numericChatId = typeof chatId === "string" ? parseInt(chatId, 10) : chatId;
13369
+ await this.bot.telegram.setMessageReaction(numericChatId, messageId, []);
13370
+ return {
13371
+ success: true,
13372
+ chatId,
13373
+ messageId,
13374
+ reaction: ""
13375
+ };
13376
+ } catch (error) {
13377
+ const errorMessage = error instanceof Error ? error.message : String(error);
13378
+ logger3.error({ error }, `Failed to remove reaction: ${errorMessage}`);
13379
+ return {
13380
+ success: false,
13381
+ chatId,
13382
+ messageId,
13383
+ reaction: "",
13384
+ error: errorMessage
13385
+ };
13386
+ }
13387
+ }
12844
13388
  static async start(runtime) {
12845
13389
  const service = new TelegramService(runtime);
12846
13390
  if (!service.bot) {
12847
13391
  logger3.warn("Telegram service started without bot functionality - no bot token provided");
12848
13392
  return service;
12849
13393
  }
13394
+ const config = await validateTelegramConfig(runtime);
13395
+ if (config) {
13396
+ service.settings = buildTelegramSettings(config);
13397
+ }
12850
13398
  const maxRetries = 5;
12851
13399
  let retryCount = 0;
12852
13400
  let lastError = null;
12853
13401
  while (retryCount < maxRetries) {
12854
13402
  try {
12855
13403
  logger3.success(`Telegram client started for character ${runtime.character.name}`);
12856
- await service.initializeBot();
13404
+ const botInfo = await service.bot?.telegram.getMe();
13405
+ if (botInfo) {
13406
+ service.botInfo = botInfo;
13407
+ logger3.info(`Bot connected: @${botInfo.username} (ID: ${botInfo.id})`);
13408
+ }
12857
13409
  service.setupMiddlewares();
12858
13410
  service.setupMessageHandlers();
12859
- await service.bot?.telegram.getMe();
13411
+ const updateMode = service.settings?.updateMode || "polling";
13412
+ if (updateMode === "webhook" && service.settings?.webhookUrl) {
13413
+ await service.initializeWebhook();
13414
+ } else {
13415
+ await service.initializePolling();
13416
+ }
13417
+ service.runtime.emitEvent("TELEGRAM_BOT_STARTED" /* BOT_STARTED */, {
13418
+ runtime: service.runtime,
13419
+ botId: service.botInfo?.id,
13420
+ botUsername: service.botInfo?.username,
13421
+ botName: service.botInfo?.first_name,
13422
+ updateMode,
13423
+ timestamp: Date.now()
13424
+ });
12860
13425
  return service;
12861
13426
  } catch (error) {
12862
13427
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -12879,9 +13444,23 @@ class TelegramService extends Service {
12879
13444
  }
12880
13445
  }
12881
13446
  async stop() {
13447
+ this.runtime.emitEvent("TELEGRAM_BOT_STOPPED" /* BOT_STOPPED */, {
13448
+ runtime: this.runtime,
13449
+ botId: this.botInfo?.id,
13450
+ botUsername: this.botInfo?.username,
13451
+ botName: this.botInfo?.first_name,
13452
+ updateMode: this.settings?.updateMode || "polling",
13453
+ timestamp: Date.now()
13454
+ });
13455
+ if (this.webhookServer) {
13456
+ await new Promise((resolve) => {
13457
+ this.webhookServer?.close(() => resolve());
13458
+ });
13459
+ this.webhookServer = null;
13460
+ }
12882
13461
  this.bot?.stop();
12883
13462
  }
12884
- async initializeBot() {
13463
+ async initializePolling() {
12885
13464
  this.bot?.start((ctx) => {
12886
13465
  this.runtime.emitEvent("TELEGRAM_SLASH_START" /* SLASH_START */, {
12887
13466
  runtime: this.runtime,
@@ -12889,17 +13468,102 @@ class TelegramService extends Service {
12889
13468
  ctx
12890
13469
  });
12891
13470
  });
13471
+ const dropPendingUpdates = this.settings?.dropPendingUpdates ?? true;
12892
13472
  this.bot?.launch({
12893
- dropPendingUpdates: true,
12894
- allowedUpdates: ["message", "message_reaction"]
13473
+ dropPendingUpdates,
13474
+ allowedUpdates: ["message", "message_reaction", "chat_member", "my_chat_member"]
12895
13475
  });
12896
- const botInfo = await this.bot?.telegram.getMe();
12897
- logger3.log(`Bot info: ${JSON.stringify(botInfo)}`);
13476
+ logger3.info(`Telegram bot started in polling mode (dropPendingUpdates: ${dropPendingUpdates})`);
12898
13477
  process.once("SIGINT", () => this.bot?.stop("SIGINT"));
12899
13478
  process.once("SIGTERM", () => this.bot?.stop("SIGTERM"));
12900
13479
  }
13480
+ async initializeWebhook() {
13481
+ if (!this.bot || !this.settings?.webhookUrl) {
13482
+ throw new Error("Bot or webhook URL not configured");
13483
+ }
13484
+ const webhookUrl = this.settings.webhookUrl;
13485
+ const webhookPath = this.settings.webhookPath || "/telegram/webhook";
13486
+ const webhookPort = this.settings.webhookPort || 3000;
13487
+ const webhookSecret = this.settings.webhookSecret;
13488
+ this.bot.start((ctx) => {
13489
+ this.runtime.emitEvent("TELEGRAM_SLASH_START" /* SLASH_START */, {
13490
+ runtime: this.runtime,
13491
+ source: "telegram",
13492
+ ctx
13493
+ });
13494
+ });
13495
+ const fullWebhookUrl = `${webhookUrl}${webhookPath}`;
13496
+ await this.bot.telegram.setWebhook(fullWebhookUrl, {
13497
+ secret_token: webhookSecret,
13498
+ allowed_updates: ["message", "message_reaction", "chat_member", "my_chat_member"],
13499
+ drop_pending_updates: this.settings.dropPendingUpdates
13500
+ });
13501
+ const webhookCallback = this.bot.webhookCallback(webhookPath, {
13502
+ secretToken: webhookSecret
13503
+ });
13504
+ const http = await import("node:http");
13505
+ this.webhookServer = http.createServer(async (req, res) => {
13506
+ if (req.url === webhookPath && req.method === "POST") {
13507
+ await webhookCallback(req, res);
13508
+ } else if (req.url === "/health" || req.url === "/") {
13509
+ res.writeHead(200, { "Content-Type": "application/json" });
13510
+ res.end(JSON.stringify({ status: "ok", bot: this.botInfo?.username }));
13511
+ } else {
13512
+ res.writeHead(404);
13513
+ res.end("Not Found");
13514
+ }
13515
+ });
13516
+ this.webhookServer.listen(webhookPort, () => {
13517
+ logger3.info(`Telegram webhook server listening on port ${webhookPort}`);
13518
+ logger3.info(`Webhook URL: ${fullWebhookUrl}`);
13519
+ });
13520
+ this.runtime.emitEvent("TELEGRAM_WEBHOOK_REGISTERED" /* WEBHOOK_REGISTERED */, {
13521
+ runtime: this.runtime,
13522
+ url: fullWebhookUrl,
13523
+ path: webhookPath,
13524
+ port: webhookPort,
13525
+ hasSecret: !!webhookSecret,
13526
+ timestamp: Date.now()
13527
+ });
13528
+ process.once("SIGINT", () => this.stop());
13529
+ process.once("SIGTERM", () => this.stop());
13530
+ }
13531
+ async deleteWebhook() {
13532
+ if (!this.bot) {
13533
+ return false;
13534
+ }
13535
+ try {
13536
+ await this.bot.telegram.deleteWebhook({ drop_pending_updates: true });
13537
+ logger3.info("Webhook deleted successfully");
13538
+ return true;
13539
+ } catch (error) {
13540
+ logger3.error({ error }, "Failed to delete webhook");
13541
+ return false;
13542
+ }
13543
+ }
13544
+ async getWebhookInfo() {
13545
+ if (!this.bot) {
13546
+ return null;
13547
+ }
13548
+ try {
13549
+ const info = await this.bot.telegram.getWebhookInfo();
13550
+ return {
13551
+ url: info.url,
13552
+ hasCustomCertificate: info.has_custom_certificate,
13553
+ pendingUpdateCount: info.pending_update_count,
13554
+ lastErrorDate: info.last_error_date,
13555
+ lastErrorMessage: info.last_error_message,
13556
+ maxConnections: info.max_connections,
13557
+ allowedUpdates: info.allowed_updates
13558
+ };
13559
+ } catch (error) {
13560
+ logger3.error({ error }, "Failed to get webhook info");
13561
+ return null;
13562
+ }
13563
+ }
12901
13564
  setupMiddlewares() {
12902
13565
  this.bot?.use(this.authorizationMiddleware.bind(this));
13566
+ this.bot?.use(this.dmAccessMiddleware.bind(this));
12903
13567
  this.bot?.use(this.chatAndEntityMiddleware.bind(this));
12904
13568
  }
12905
13569
  async authorizationMiddleware(ctx, next) {
@@ -12909,6 +13573,75 @@ class TelegramService extends Service {
12909
13573
  }
12910
13574
  await next();
12911
13575
  }
13576
+ async dmAccessMiddleware(ctx, next) {
13577
+ if (ctx.chat?.type !== "private") {
13578
+ return next();
13579
+ }
13580
+ const accessResult = await this.checkDmAccess(ctx);
13581
+ if (!accessResult.allowed) {
13582
+ if (accessResult.replyMessage && ctx.from) {
13583
+ try {
13584
+ await this.bot?.telegram.sendMessage(ctx.from.id, accessResult.replyMessage);
13585
+ } catch (err) {
13586
+ logger3.warn({ error: err instanceof Error ? err.message : String(err), userId: ctx.from.id }, "Failed to send pairing reply");
13587
+ }
13588
+ }
13589
+ return;
13590
+ }
13591
+ await next();
13592
+ }
13593
+ async checkDmAccess(ctx) {
13594
+ if (!ctx.from) {
13595
+ return { allowed: false };
13596
+ }
13597
+ const account = resolveTelegramAccount(this.runtime);
13598
+ const dmConfig = account.config.dm;
13599
+ const policy = dmConfig?.policy ?? "open";
13600
+ const userId = ctx.from.id.toString();
13601
+ if (policy === "disabled" || dmConfig?.enabled === false) {
13602
+ logger3.debug({ userId }, "DM blocked: policy is disabled");
13603
+ return { allowed: false };
13604
+ }
13605
+ if (policy === "open") {
13606
+ return { allowed: true };
13607
+ }
13608
+ if (policy === "allowlist") {
13609
+ const allowFrom = dmConfig?.allowFrom ?? [];
13610
+ if (allowFrom.some((a) => String(a) === userId || String(a) === ctx.from?.username)) {
13611
+ return { allowed: true };
13612
+ }
13613
+ const inDynamicAllowlist = await isInAllowlist(this.runtime, "telegram", userId);
13614
+ if (inDynamicAllowlist) {
13615
+ return { allowed: true };
13616
+ }
13617
+ logger3.debug({ userId }, "DM blocked: user not in allowlist");
13618
+ return { allowed: false };
13619
+ }
13620
+ if (policy === "pairing") {
13621
+ const allowFrom = dmConfig?.allowFrom ?? [];
13622
+ if (allowFrom.some((a) => String(a) === userId || String(a) === ctx.from?.username)) {
13623
+ return { allowed: true };
13624
+ }
13625
+ const result = await checkPairingAllowed(this.runtime, {
13626
+ channel: "telegram",
13627
+ senderId: userId,
13628
+ metadata: {
13629
+ username: ctx.from.username ?? "",
13630
+ firstName: ctx.from.first_name ?? "",
13631
+ lastName: ctx.from.last_name ?? ""
13632
+ }
13633
+ });
13634
+ if (result.allowed) {
13635
+ return { allowed: true };
13636
+ }
13637
+ logger3.debug({ userId, pairingCode: result.pairingCode, newRequest: result.newRequest }, "DM blocked: pairing required");
13638
+ return {
13639
+ allowed: false,
13640
+ replyMessage: result.newRequest ? result.replyMessage : undefined
13641
+ };
13642
+ }
13643
+ return { allowed: true };
13644
+ }
12912
13645
  async chatAndEntityMiddleware(ctx, next) {
12913
13646
  if (!ctx.chat)
12914
13647
  return next();
@@ -13515,25 +14248,306 @@ class TelegramTestSuite {
13515
14248
  }
13516
14249
  }
13517
14250
  }
14251
+ // src/formatting.ts
14252
+ function escapeHtml(text) {
14253
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14254
+ }
14255
+ function escapeHtmlAttr(text) {
14256
+ return escapeHtml(text).replace(/"/g, "&quot;");
14257
+ }
14258
+ function escapeMarkdownV2(text) {
14259
+ return text.replace(/([_*[\]()~`>#+\-=|{}.!\\])/g, "\\$1");
14260
+ }
14261
+ function convertBold(text) {
14262
+ return text.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
14263
+ }
14264
+ function convertItalic(text) {
14265
+ return text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<i>$1</i>");
14266
+ }
14267
+ function convertUnderline(text) {
14268
+ return text.replace(/__(.+?)__/g, "<u>$1</u>");
14269
+ }
14270
+ function convertStrikethrough(text) {
14271
+ return text.replace(/~~(.+?)~~/g, "<s>$1</s>");
14272
+ }
14273
+ function convertInlineCode(text) {
14274
+ return text.replace(/`([^`\n]+)`/g, (_, code) => `<code>${escapeHtml(code)}</code>`);
14275
+ }
14276
+ function convertCodeBlocks(text) {
14277
+ return text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
14278
+ const escapedCode = escapeHtml(code.trim());
14279
+ if (lang) {
14280
+ return `<pre><code class="language-${escapeHtmlAttr(lang)}">${escapedCode}</code></pre>`;
14281
+ }
14282
+ return `<pre><code>${escapedCode}</code></pre>`;
14283
+ });
14284
+ }
14285
+ function convertLinks(text) {
14286
+ return text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, linkText, url) => {
14287
+ const safeUrl = escapeHtmlAttr(url.trim());
14288
+ return `<a href="${safeUrl}">${escapeHtml(linkText)}</a>`;
14289
+ });
14290
+ }
14291
+ function linkifyUrls(text) {
14292
+ const urlRegex = /(?<!["'])(https?:\/\/[^\s<>"']+)/g;
14293
+ return text.replace(urlRegex, (url) => {
14294
+ const safeUrl = escapeHtmlAttr(url);
14295
+ return `<a href="${safeUrl}">${escapeHtml(url)}</a>`;
14296
+ });
14297
+ }
14298
+ function markdownToTelegramHtml(markdown, options = {}) {
14299
+ if (!markdown) {
14300
+ return "";
14301
+ }
14302
+ let result = markdown;
14303
+ result = convertCodeBlocks(result);
14304
+ result = convertInlineCode(result);
14305
+ result = convertLinks(result);
14306
+ const htmlTagPattern = /(<(?:b|i|u|s|code|pre|a)[^>]*>[\s\S]*?<\/(?:b|i|u|s|code|pre|a)>)/g;
14307
+ const parts = result.split(htmlTagPattern);
14308
+ result = parts.map((part, index) => {
14309
+ if (index % 2 === 0) {
14310
+ let text = escapeHtml(part);
14311
+ text = convertBold(text);
14312
+ text = convertItalic(text);
14313
+ text = convertUnderline(text);
14314
+ text = convertStrikethrough(text);
14315
+ if (options.linkify !== false) {
14316
+ text = linkifyUrls(text);
14317
+ }
14318
+ return text;
14319
+ }
14320
+ return part;
14321
+ }).join("");
14322
+ return result;
14323
+ }
14324
+ function markdownToTelegramChunks(markdown, limit, options = {}) {
14325
+ const html = markdownToTelegramHtml(markdown, options);
14326
+ const chunks = chunkTelegramText(html, limit);
14327
+ return chunks.map((chunk) => ({
14328
+ html: chunk,
14329
+ text: stripHtmlTags(chunk)
14330
+ }));
14331
+ }
14332
+ function markdownToTelegramHtmlChunks(markdown, limit) {
14333
+ return markdownToTelegramChunks(markdown, limit).map((chunk) => chunk.html);
14334
+ }
14335
+ function stripHtmlTags(html) {
14336
+ return html.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"');
14337
+ }
14338
+ var DEFAULT_MAX_CHARS = 4096;
14339
+ function chunkTelegramText(text, maxChars = DEFAULT_MAX_CHARS) {
14340
+ if (!text) {
14341
+ return [];
14342
+ }
14343
+ if (text.length <= maxChars) {
14344
+ return [text];
14345
+ }
14346
+ const chunks = [];
14347
+ let remaining = text;
14348
+ while (remaining.length > 0) {
14349
+ if (remaining.length <= maxChars) {
14350
+ chunks.push(remaining);
14351
+ break;
14352
+ }
14353
+ let breakPoint = maxChars;
14354
+ const newlineIndex = remaining.lastIndexOf(`
14355
+ `, maxChars);
14356
+ if (newlineIndex > maxChars * 0.5) {
14357
+ breakPoint = newlineIndex + 1;
14358
+ } else {
14359
+ const spaceIndex = remaining.lastIndexOf(" ", maxChars);
14360
+ if (spaceIndex > maxChars * 0.5) {
14361
+ breakPoint = spaceIndex + 1;
14362
+ }
14363
+ }
14364
+ const chunk = remaining.slice(0, breakPoint);
14365
+ const openTags = chunk.match(/<(\w+)[^>]*>/g) || [];
14366
+ const closeTags = chunk.match(/<\/(\w+)>/g) || [];
14367
+ const tagStack = [];
14368
+ for (const tag of openTags) {
14369
+ const tagName = tag.match(/<(\w+)/)?.[1];
14370
+ if (tagName && !tag.endsWith("/>")) {
14371
+ tagStack.push(tagName);
14372
+ }
14373
+ }
14374
+ for (const tag of closeTags) {
14375
+ const tagName = tag.match(/<\/(\w+)>/)?.[1];
14376
+ if (tagName) {
14377
+ const index = tagStack.lastIndexOf(tagName);
14378
+ if (index !== -1) {
14379
+ tagStack.splice(index, 1);
14380
+ }
14381
+ }
14382
+ }
14383
+ let chunkWithClosedTags = chunk;
14384
+ const closingTags = tagStack.slice().reverse().map((tag) => `</${tag}>`).join("");
14385
+ chunkWithClosedTags += closingTags;
14386
+ chunks.push(chunkWithClosedTags);
14387
+ const openingTags = tagStack.map((tag) => `<${tag}>`).join("");
14388
+ remaining = openingTags + remaining.slice(breakPoint);
14389
+ }
14390
+ return chunks;
14391
+ }
14392
+ function formatTelegramUser(user) {
14393
+ if (user.username) {
14394
+ return `@${user.username}`;
14395
+ }
14396
+ if (user.last_name) {
14397
+ return `${user.first_name} ${user.last_name}`;
14398
+ }
14399
+ return user.first_name;
14400
+ }
14401
+ function formatTelegramUserMention(user) {
14402
+ const displayName = formatTelegramUser(user);
14403
+ return `<a href="tg://user?id=${user.id}">${escapeHtml(displayName)}</a>`;
14404
+ }
14405
+ function formatTelegramChat(chat) {
14406
+ if (chat.type === "private") {
14407
+ const privateChat = chat;
14408
+ if (privateChat.username) {
14409
+ return `@${privateChat.username}`;
14410
+ }
14411
+ if (privateChat.last_name) {
14412
+ return `${privateChat.first_name} ${privateChat.last_name}`;
14413
+ }
14414
+ return privateChat.first_name || `User ${chat.id}`;
14415
+ }
14416
+ const groupChat = chat;
14417
+ if ("username" in groupChat && groupChat.username) {
14418
+ return `@${groupChat.username}`;
14419
+ }
14420
+ return groupChat.title || `Chat ${chat.id}`;
14421
+ }
14422
+ function getChatTypeString(chat) {
14423
+ switch (chat.type) {
14424
+ case "private":
14425
+ return "DM";
14426
+ case "group":
14427
+ return "Group";
14428
+ case "supergroup":
14429
+ return "Supergroup";
14430
+ case "channel":
14431
+ return "Channel";
14432
+ default:
14433
+ return "Unknown";
14434
+ }
14435
+ }
14436
+ function resolveTelegramSystemLocation(chat) {
14437
+ const chatType = getChatTypeString(chat);
14438
+ const chatName = formatTelegramChat(chat);
14439
+ return `${chatType}: ${chatName}`;
14440
+ }
14441
+ function isPrivateChat(chat) {
14442
+ return chat.type === "private";
14443
+ }
14444
+ function isGroupChat(chat) {
14445
+ return chat.type === "group" || chat.type === "supergroup";
14446
+ }
14447
+ function isChannelChat(chat) {
14448
+ return chat.type === "channel";
14449
+ }
14450
+ function truncateText(text, maxLength, ellipsis = "…") {
14451
+ if (text.length <= maxLength) {
14452
+ return text;
14453
+ }
14454
+ return text.slice(0, maxLength - ellipsis.length) + ellipsis;
14455
+ }
14456
+ function buildTelegramDeepLink(botUsername, startParam) {
14457
+ const base = `https://t.me/${botUsername}`;
14458
+ if (startParam) {
14459
+ return `${base}?start=${encodeURIComponent(startParam)}`;
14460
+ }
14461
+ return base;
14462
+ }
14463
+ function buildTelegramMessageLink(chatUsername, messageId) {
14464
+ return `https://t.me/${chatUsername}/${messageId}`;
14465
+ }
14466
+ function parseTelegramMessageLink(url) {
14467
+ const match = url.match(/^https?:\/\/t\.me\/([^/]+)\/(\d+)$/);
14468
+ if (!match) {
14469
+ return null;
14470
+ }
14471
+ return {
14472
+ chatUsername: match[1],
14473
+ messageId: parseInt(match[2], 10)
14474
+ };
14475
+ }
14476
+ function formatMediaCaption(text, maxLength = 1024) {
14477
+ if (!text) {
14478
+ return "";
14479
+ }
14480
+ if (text.length <= maxLength) {
14481
+ return text;
14482
+ }
14483
+ let breakPoint = maxLength - 1;
14484
+ const newlineIndex = text.lastIndexOf(`
14485
+ `, maxLength);
14486
+ if (newlineIndex > maxLength * 0.5) {
14487
+ breakPoint = newlineIndex;
14488
+ } else {
14489
+ const spaceIndex = text.lastIndexOf(" ", maxLength);
14490
+ if (spaceIndex > maxLength * 0.5) {
14491
+ breakPoint = spaceIndex;
14492
+ }
14493
+ }
14494
+ return `${text.slice(0, breakPoint)}…`;
14495
+ }
13518
14496
 
13519
14497
  // src/index.ts
13520
14498
  var telegramPlugin = {
13521
14499
  name: TELEGRAM_SERVICE_NAME,
13522
- description: "Telegram client plugin",
14500
+ description: "Telegram client plugin with polling and webhook support",
13523
14501
  services: [TelegramService],
13524
- actions: [sendMessageAction],
14502
+ actions: [sendMessageAction, sendReactionAction],
13525
14503
  providers: [chatStateProvider],
13526
14504
  tests: [new TelegramTestSuite]
13527
14505
  };
13528
14506
  var src_default = telegramPlugin;
13529
14507
  export {
14508
+ validateTelegramConfig,
14509
+ truncateText,
14510
+ stripHtmlTags,
14511
+ sendReactionAction,
13530
14512
  sendMessageAction,
14513
+ resolveTelegramToken,
14514
+ resolveTelegramSystemLocation,
14515
+ resolveTelegramAccount,
14516
+ resolveDefaultTelegramAccountId,
14517
+ parseTelegramMessageLink,
14518
+ normalizeTelegramToken,
14519
+ normalizeAccountId,
14520
+ markdownToTelegramHtmlChunks,
14521
+ markdownToTelegramHtml,
14522
+ markdownToTelegramChunks,
14523
+ listTelegramAccountIds,
14524
+ listEnabledTelegramAccounts,
14525
+ isPrivateChat,
14526
+ isMultiAccountEnabled,
14527
+ isGroupChat,
14528
+ isChannelChat,
14529
+ getChatTypeString,
14530
+ formatTelegramUserMention,
14531
+ formatTelegramUser,
14532
+ formatTelegramChat,
14533
+ formatMediaCaption,
14534
+ escapeMarkdownV2,
14535
+ escapeHtmlAttr,
14536
+ escapeHtml,
13531
14537
  src_default as default,
14538
+ chunkTelegramText,
13532
14539
  chatStateProvider,
14540
+ buildTelegramSettings,
14541
+ buildTelegramMessageLink,
14542
+ buildTelegramDeepLink,
13533
14543
  TelegramService,
14544
+ TelegramEventTypes,
14545
+ TELEGRAM_REACTIONS,
14546
+ SEND_REACTION_ACTION,
13534
14547
  SEND_MESSAGE_ACTION,
13535
14548
  MessageManager,
14549
+ DEFAULT_ACCOUNT_ID,
13536
14550
  CHAT_STATE_PROVIDER
13537
14551
  };
13538
14552
 
13539
- //# debugId=7D6DD157C30C505564756E2164756E21
14553
+ //# debugId=5EB5738096CCF54A64756E2164756E21