polygram 0.7.4 → 0.7.5

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "polygram",
4
- "version": "0.7.4",
4
+ "version": "0.7.5",
5
5
  "description": "Telegram integration for Claude Code that preserves the OpenClaw per-chat session model. Migration target for OpenClaw users. Multi-bot, multi-chat, per-topic isolation; SQLite transcripts; inline-keyboard approvals. Bundles /polygram:status|logs|pair-code|approvals admin commands and a history skill.",
6
6
  "keywords": [
7
7
  "telegram",
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Parse Claude's final-turn text into one of three outbound shapes:
3
+ * - sticker (single emoji that maps to a sticker, OR literal
4
+ * `[sticker:NAME]` mimic — see below)
5
+ * - reaction (single emoji not mapped to a sticker)
6
+ * - text (everything else)
7
+ *
8
+ * Why this lives in lib/: polygram.js is a top-level script (calls main()
9
+ * at bottom) and can't be require()'d from a test without starting a bot.
10
+ * Pulling parseResponse out lets tests cover the regex edge cases.
11
+ *
12
+ * 0.7.5 (item: sticker regression):
13
+ * deriveOutboundText (lib/telegram.js) synthesises `[sticker:<name>]` for
14
+ * sendSticker calls so the messages.text column has *something* legible.
15
+ * On session resume Claude reads its own past assistant rows and sees
16
+ * `[sticker:working]` as the assistant message text — and starts mimicking
17
+ * the format LITERALLY, emitting the string `[sticker:working]` as plain
18
+ * text. parseResponse used to fall through to the chunked-text path, so
19
+ * the placeholder ended up rendered in the user's chat instead of an
20
+ * actual sticker.
21
+ *
22
+ * Match shape: optional whitespace, `[sticker:`, NAME (alnum/_/-), `]`,
23
+ * optional whitespace. NAME must resolve in the supplied stickerMap;
24
+ * unknown NAMEs fall through to the text path so a genuine
25
+ * "[sticker:foo]" message (e.g. someone joking, or a stale name from an
26
+ * older deploy) still reaches the user verbatim.
27
+ */
28
+
29
+ const STICKER_TAG_RE = /^\s*\[sticker:([A-Za-z0-9_-]+)\]\s*$/;
30
+
31
+ function parseResponse(text, { stickerMap = {}, emojiToSticker = {} } = {}) {
32
+ const trimmed = (text || '').trim();
33
+
34
+ const tagMatch = trimmed.match(STICKER_TAG_RE);
35
+ if (tagMatch) {
36
+ const name = tagMatch[1];
37
+ const fileId = stickerMap[name];
38
+ if (fileId) {
39
+ return { text: '', sticker: fileId, stickerLabel: name, reaction: null };
40
+ }
41
+ }
42
+
43
+ const emojiOnly = /^\p{Emoji_Presentation}$/u.test(trimmed)
44
+ || /^\p{Emoji}️?$/u.test(trimmed);
45
+
46
+ if (emojiOnly && trimmed) {
47
+ if (emojiToSticker[trimmed]) {
48
+ return { text: '', sticker: emojiToSticker[trimmed], stickerLabel: trimmed, reaction: null };
49
+ }
50
+ return { text: '', sticker: null, stickerLabel: null, reaction: trimmed };
51
+ }
52
+
53
+ return { text: trimmed, sticker: null, stickerLabel: null, reaction: null };
54
+ }
55
+
56
+ module.exports = { parseResponse, STICKER_TAG_RE };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Telegram daemon for Claude Code that preserves the OpenClaw per-chat session model. Migration path for OpenClaw users moving to Claude Code.",
5
5
  "main": "lib/ipc-client.js",
6
6
  "bin": {
package/polygram.js CHANGED
@@ -961,19 +961,21 @@ const stdinLock = createAsyncLock();
961
961
  // hammering sendChatAction every 4s for the full turn duration.
962
962
 
963
963
  // ─── Response parsing (stickers, reactions) ─────────────────────────
964
-
964
+ // Implementation lives in lib/parse-response.js so tests can require it
965
+ // without starting a bot (polygram.js is a top-level script that calls
966
+ // main() at bottom). The wrapper here supplies the runtime stickerMap /
967
+ // emojiToSticker that the parser looks up against.
968
+ //
969
+ // 0.7.5: parser also recognises a literal `[sticker:NAME]` pattern in
970
+ // addition to single-emoji shortcuts. Claude reads its own past outbound
971
+ // rows on session resume, sees `[sticker:working]` (the placeholder
972
+ // deriveOutboundText synthesises for sendSticker rows), and starts
973
+ // mimicking the format as plain text. Without the new branch the
974
+ // placeholder was rendered verbatim in the chat instead of swapped for
975
+ // the actual sticker.
976
+ const { parseResponse: parseResponseImpl } = require('./lib/parse-response');
965
977
  function parseResponse(text) {
966
- const trimmed = text.trim();
967
- const emojiOnly = /^\p{Emoji_Presentation}$/u.test(trimmed) || /^\p{Emoji}\uFE0F?$/u.test(trimmed);
968
-
969
- if (emojiOnly && trimmed) {
970
- if (emojiToSticker[trimmed]) {
971
- return { text: '', sticker: emojiToSticker[trimmed], stickerLabel: trimmed, reaction: null };
972
- }
973
- return { text: '', sticker: null, stickerLabel: null, reaction: trimmed };
974
- }
975
-
976
- return { text: trimmed, sticker: null, stickerLabel: null, reaction: null };
978
+ return parseResponseImpl(text, { stickerMap, emojiToSticker });
977
979
  }
978
980
 
979
981
  // ─── Cron/IPC send ─────────────────────────────────────────────────