clawgram 2.3.2 → 2.4.0

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
@@ -121,6 +121,11 @@ const createChannelPlugin = (runtimes) => {
121
121
  enabled: account?.enabled,
122
122
  accountId,
123
123
  proxy: (0, proxy_config_1.resolveProxyConfig)(account?.proxy),
124
+ // Field-by-field construction means every new account setting has to
125
+ // be listed here as well: 2.3.1 shipped replyParseMode read by the
126
+ // client from a config object this function had already stripped it
127
+ // from, so the setting validated, deployed and did nothing.
128
+ replyParseMode: account?.replyParseMode,
124
129
  };
125
130
  },
126
131
  },
@@ -465,6 +470,12 @@ const createChannelPlugin = (runtimes) => {
465
470
  MessageSidFull: normalized.messageId,
466
471
  Timestamp: normalized.timestamp,
467
472
  ReplyToId: normalized.replyToMessageId,
473
+ // Core renders these itself as `[Replying to: "…"]` ahead of the
474
+ // user body — it keys off Provider being "telegram", which is set
475
+ // below. Without them a highlighted reply reaches the agent as
476
+ // bare text, and the fragment the person pointed at is lost.
477
+ ReplyToQuoteText: normalized.replyQuoteText,
478
+ ReplyToIsQuote: normalized.replyIsQuote,
468
479
  MessageThreadId: normalized.messageThreadId,
469
480
  NativeChannelId: normalized.chatId,
470
481
  OriginatingChannel: "clawgram",
@@ -741,6 +752,10 @@ const createChannelPlugin = (runtimes) => {
741
752
  SenderUsername: normalized.senderUsername,
742
753
  SenderName: normalized.senderDisplay,
743
754
  ReplyToId: normalized.replyToMessageId,
755
+ // Same reason as the group path: highlighted replies happen in
756
+ // direct messages too, and the fragment is not part of the text.
757
+ ReplyToQuoteText: normalized.replyQuoteText,
758
+ ReplyToIsQuote: normalized.replyIsQuote,
744
759
  NativeChannelId: normalized.chatId,
745
760
  },
746
761
  deliver: async (payload) => {
package/dist/history.js CHANGED
@@ -227,6 +227,14 @@ function resolveSenderDisplay(msg) {
227
227
  return joined;
228
228
  return readSender(msg, "title") ?? readSender(msg, "username");
229
229
  }
230
+ /**
231
+ * Same rule as the inbound path (`normalize.ts`): the text is the evidence,
232
+ * not the `quote` flag, and blank text means no highlight.
233
+ */
234
+ function resolveHistoryReplyQuote(msg) {
235
+ const raw = msg?.replyTo?.quoteText ?? msg?.quoteText;
236
+ return typeof raw === "string" && raw.trim() ? raw : undefined;
237
+ }
230
238
  /**
231
239
  * Deliberately drops `raw`. Inbound events carry it because the runtime may need
232
240
  * the original object; history is read straight into a model's context, where an
@@ -258,6 +266,7 @@ function normalizeHistoryMessage(msg, fallbackChatId) {
258
266
  timestamp,
259
267
  sentAt: timestamp === undefined ? undefined : new Date(timestamp * 1000).toISOString(),
260
268
  replyToMessageId: toStringId(msg?.replyTo?.replyToMsgId) ?? toStringId(msg?.replyToMsgId),
269
+ replyQuoteText: resolveHistoryReplyQuote(msg),
261
270
  messageThreadId: toStringId(msg?.replyTo?.replyToTopId) ?? toStringId(msg?.replyToTopId),
262
271
  media: (0, media_1.describeMedia)(msg?.media),
263
272
  isOutgoing: msg?.out === true,
package/dist/normalize.js CHANGED
@@ -66,6 +66,21 @@ function resolveMessageThreadId(msg) {
66
66
  return (toStringId(msg?.replyTo?.replyToMsgId) ??
67
67
  toStringId(msg?.replyToMsgId));
68
68
  }
69
+ /**
70
+ * The highlighted fragment of a reply, when there is one.
71
+ *
72
+ * Telegram sends the flag and the text separately, and clients in the wild are
73
+ * not consistent about the flag. The text is the evidence: if `quoteText` has
74
+ * content, a fragment was highlighted regardless of what `quote` says. Blank
75
+ * text is treated as no highlight rather than as an empty one.
76
+ */
77
+ function resolveReplyQuote(msg) {
78
+ const raw = msg?.replyTo?.quoteText ?? msg?.quoteText;
79
+ const text = typeof raw === "string" && raw.trim() ? raw : undefined;
80
+ if (!text)
81
+ return {};
82
+ return { text, isQuote: true };
83
+ }
69
84
  function normalizeTelegramEvent(event, accountId) {
70
85
  const msg = event?.message;
71
86
  if (!msg)
@@ -84,6 +99,7 @@ function normalizeTelegramEvent(event, accountId) {
84
99
  const replyToMessageId = toStringId(msg.replyTo?.replyToMsgId) ??
85
100
  toStringId(msg.replyToMsgId);
86
101
  const messageThreadId = resolveMessageThreadId(msg);
102
+ const replyQuote = resolveReplyQuote(msg);
87
103
  const text = typeof msg.message === "string"
88
104
  ? msg.message
89
105
  : typeof msg.text === "string"
@@ -116,6 +132,8 @@ function normalizeTelegramEvent(event, accountId) {
116
132
  messageId,
117
133
  text,
118
134
  replyToMessageId,
135
+ replyQuoteText: replyQuote.text,
136
+ replyIsQuote: replyQuote.isQuote,
119
137
  chatType,
120
138
  timestamp: toTimestamp(msg.date),
121
139
  isOutgoing: Boolean(msg.out),
@@ -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.3.2",
5
+ "version": "2.4.0",
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.3.2",
3
+ "version": "2.4.0",
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": {