clawgram 2.20.0 → 2.20.2

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/channel.js CHANGED
@@ -801,7 +801,11 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
801
801
  text,
802
802
  message: rawMessage,
803
803
  });
804
- const wasReplyToSelf = await (0, helpers_1.isReplyToSelfMessage)(rawMessage, selfId);
804
+ // One fetch serves two needs: the reply-to-self gate below and
805
+ // the parent's text for the agent (ReplyToBody), which a plain
806
+ // reply does not carry on its own.
807
+ const replyParent = await (0, helpers_1.resolveReplyParent)(rawMessage, { selfId, selfLabel });
808
+ const wasReplyToSelf = replyParent.isSelf;
805
809
  const mentionDecision = (0, channel_inbound_1.resolveInboundMentionDecision)({
806
810
  facts: {
807
811
  canDetectMention: true,
@@ -875,6 +879,10 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
875
879
  // bare text, and the fragment the person pointed at is lost.
876
880
  ReplyToQuoteText: normalized.replyQuoteText,
877
881
  ReplyToIsQuote: normalized.replyIsQuote,
882
+ // A plain reply has no highlight; core then falls back to the
883
+ // parent's body, which only exists if the channel fetched it.
884
+ ReplyToBody: replyParent.body,
885
+ ReplyToSender: replyParent.sender,
878
886
  MessageThreadId: normalized.messageThreadId,
879
887
  NativeChannelId: normalized.chatId,
880
888
  // Trusted per-group prompt block from `groups.<id>.systemPrompt`.
@@ -1179,6 +1187,10 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1179
1187
  });
1180
1188
  return;
1181
1189
  }
1190
+ // Same fetch as the group path. In a DM the parent is as often
1191
+ // the agent's own message as the person's — the owner answers a
1192
+ // notice she sent — and neither text is available any other way.
1193
+ const replyParent = await (0, helpers_1.resolveReplyParent)(rawMessage, { selfId, selfLabel });
1182
1194
  await gram.withTyping(conversationTarget, async () => {
1183
1195
  await (0, direct_dm_1.dispatchInboundDirectDmWithRuntime)({
1184
1196
  cfg,
@@ -1210,6 +1222,8 @@ const createChannelPlugin = (runtimes, pluginRuntime) => {
1210
1222
  // direct messages too, and the fragment is not part of the text.
1211
1223
  ReplyToQuoteText: normalized.replyQuoteText,
1212
1224
  ReplyToIsQuote: normalized.replyIsQuote,
1225
+ ReplyToBody: replyParent.body,
1226
+ ReplyToSender: replyParent.sender,
1213
1227
  NativeChannelId: normalized.chatId,
1214
1228
  },
1215
1229
  deliver: async (payload) => {
package/dist/helpers.js CHANGED
@@ -36,6 +36,7 @@ exports.prefixReplyTextToAddress = prefixReplyTextToAddress;
36
36
  exports.resolveReplyTarget = resolveReplyTarget;
37
37
  exports.resolveChatTarget = resolveChatTarget;
38
38
  exports.isReplyToSelfMessage = isReplyToSelfMessage;
39
+ exports.resolveReplyParent = resolveReplyParent;
39
40
  exports.resolveSenderProfile = resolveSenderProfile;
40
41
  exports.resolveSenderProfileWithTimeout = resolveSenderProfileWithTimeout;
41
42
  exports.normalizeParseMode = normalizeParseMode;
@@ -549,27 +550,58 @@ async function resolveChatTarget(message) {
549
550
  }
550
551
  return message?.inputChat ?? message?._inputChat ?? message?.chat ?? message?._chat ?? message?.peerId;
551
552
  }
552
- async function isReplyToSelfMessage(message, selfId) {
553
- if (!selfId) {
554
- return false;
555
- }
553
+ /**
554
+ * The message a reply points at, fetched once.
555
+ *
556
+ * Telegram does not put the parent's text into the reply; a highlight
557
+ * (`quoteText`) is the only fragment that travels with it, and most replies
558
+ * have none. Core renders `[Replying to: …]` from the highlight or, failing
559
+ * that, from `ReplyToBody` — so without this fetch a plain reply reaches the
560
+ * agent as a bare parent id. The case that exposed it: the owner answered, in
561
+ * a DM, the agent's own notice about an unknown sender; the notice had been
562
+ * sent from another session, DMs are outside `readChats`, and the agent had
563
+ * no way to learn what "reply to #1011" referred to.
564
+ *
565
+ * Failure degrades to "no context" on purpose: a parent that cannot be
566
+ * fetched (deleted, flood-waited, transport hiccup) must not cost the
567
+ * message itself.
568
+ */
569
+ async function resolveReplyParent(message, input) {
556
570
  const replyToMessageId = message?.replyTo?.replyToMsgId ?? message?.replyToMsgId;
557
571
  if (!replyToMessageId) {
558
- return false;
572
+ return { isSelf: false };
559
573
  }
560
574
  const replied = typeof message?.getReplyMessage === "function"
561
575
  ? await message.getReplyMessage().catch(() => undefined)
562
576
  : undefined;
563
577
  if (!replied) {
564
- return false;
565
- }
566
- if (replied.out === true) {
567
- return true;
578
+ return { isSelf: false };
568
579
  }
569
580
  const replySenderId = replied.senderId ??
570
581
  replied.fromId?.userId ??
571
582
  replied.fromId?.channelId;
572
- return replySenderId !== undefined && String(replySenderId) === selfId;
583
+ const isSelf = replied.out === true ||
584
+ (input.selfId !== undefined && replySenderId !== undefined && String(replySenderId) === input.selfId);
585
+ const rawText = typeof replied.message === "string" ? replied.message :
586
+ typeof replied.text === "string" ? replied.text :
587
+ "";
588
+ const body = rawText.trim() ? rawText : undefined;
589
+ const source = replied.sender ?? replied._sender;
590
+ const sender = isSelf
591
+ ? (input.selfLabel ?? (input.selfId !== undefined ? input.selfId : undefined))
592
+ : toDisplayName({
593
+ firstName: typeof source?.firstName === "string" ? source.firstName : undefined,
594
+ lastName: typeof source?.lastName === "string" ? source.lastName : undefined,
595
+ username: undefined,
596
+ fallback: resolveActiveUsername(source) ? `@${resolveActiveUsername(source)}` : (replySenderId !== undefined ? String(replySenderId) : undefined),
597
+ });
598
+ return { isSelf, body, sender: sender || undefined };
599
+ }
600
+ async function isReplyToSelfMessage(message, selfId) {
601
+ if (!selfId) {
602
+ return false;
603
+ }
604
+ return (await resolveReplyParent(message, { selfId })).isSelf;
573
605
  }
574
606
  async function resolveSenderProfile(message, input) {
575
607
  const pickProfile = (source) => {
@@ -2,7 +2,7 @@
2
2
  "id": "clawgram",
3
3
  "name": "Clawgram",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
- "version": "2.20.0",
5
+ "version": "2.20.2",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawgram",
3
- "version": "2.20.0",
3
+ "version": "2.20.2",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {