@mulmobridge/telegram 0.1.2 → 1.0.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/README.md CHANGED
@@ -32,6 +32,24 @@ yarn telegram
32
32
  | `MULMOCLAUDE_AUTH_TOKEN` | No | Bearer token override | reads from file |
33
33
  | `TELEGRAM_POLL_TIMEOUT_SEC` | No | Long-poll timeout | `25` |
34
34
 
35
+ ### Auth token persistence across server restarts
36
+
37
+ The MulmoClaude server regenerates a fresh bearer token on every startup and writes it to `~/mulmoclaude/.session-token`. The bridge reads that file once at launch and keeps the token in memory — so if the server restarts while the bridge is running, the bridge keeps using the **old** token and every API call returns **401**, silently.
38
+
39
+ **Fix**: set `MULMOCLAUDE_AUTH_TOKEN` to the same long random value on **both** the server and the bridge. The server uses it verbatim instead of regenerating, so the token survives restarts and the bridge stays authenticated.
40
+
41
+ ```bash
42
+ # Server (one-time setup — same value across restarts)
43
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string yarn dev
44
+
45
+ # Bridge (separate process / machine — same value)
46
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string \
47
+ <bridge-specific-envs> \
48
+ npx <this-package>@latest
49
+ ```
50
+
51
+ Recommended: at least 32 characters of random data (the server logs a warning at startup for shorter values).
52
+
35
53
  ## Security
36
54
 
37
55
  The bridge enforces a **chat-ID allowlist**. Only messages from listed chat IDs are forwarded to MulmoClaude. All other messages receive a one-time "Access denied" reply.
@@ -46,13 +64,43 @@ The bridge enforces a **chat-ID allowlist**. Only messages from listed chat IDs
46
64
 
47
65
  ## Ecosystem
48
66
 
49
- Part of the `@mulmobridge/*` package family:
50
-
51
- - **@mulmobridge/protocol** — shared types and constants
52
- - **@mulmobridge/client** — socket.io client library
53
- - **@mulmobridge/cli** — interactive terminal bridge
54
- - **@mulmobridge/telegram** — this package
55
- - **@mulmobridge/chat-service** — server-side chat service
67
+ Part of the [`@mulmobridge/*`](https://www.npmjs.com/~mulmobridge) package family.
68
+
69
+ **Shared libraries:**
70
+
71
+ - [`@mulmobridge/client`](https://www.npmjs.com/package/@mulmobridge/client) — socket.io client library used by every bridge below
72
+ - [`@mulmobridge/protocol`](https://www.npmjs.com/package/@mulmobridge/protocol) — wire types and constants
73
+ - [`@mulmobridge/chat-service`](https://www.npmjs.com/package/@mulmobridge/chat-service) — server-side relay + session store
74
+ - [`@mulmobridge/relay`](https://www.npmjs.com/package/@mulmobridge/relay) — Cloudflare Workers webhook proxy
75
+ - [`@mulmobridge/mock-server`](https://www.npmjs.com/package/@mulmobridge/mock-server) — mock server for local bridge development
76
+
77
+ **Bridges** (one npm package per platform):
78
+
79
+ - [`@mulmobridge/bluesky`](https://www.npmjs.com/package/@mulmobridge/bluesky) — Bluesky DMs over atproto
80
+ - [`@mulmobridge/chatwork`](https://www.npmjs.com/package/@mulmobridge/chatwork) — Chatwork (Japanese business chat)
81
+ - [`@mulmobridge/cli`](https://www.npmjs.com/package/@mulmobridge/cli) — interactive terminal bridge
82
+ - [`@mulmobridge/discord`](https://www.npmjs.com/package/@mulmobridge/discord) — Discord bot via Gateway
83
+ - [`@mulmobridge/email`](https://www.npmjs.com/package/@mulmobridge/email) — IMAP poll + SMTP reply, threading preserved
84
+ - [`@mulmobridge/google-chat`](https://www.npmjs.com/package/@mulmobridge/google-chat) — Google Chat via MulmoBridge relay
85
+ - [`@mulmobridge/irc`](https://www.npmjs.com/package/@mulmobridge/irc) — IRC (Libera, Freenode, custom)
86
+ - [`@mulmobridge/line`](https://www.npmjs.com/package/@mulmobridge/line) — LINE Messaging API via MulmoBridge relay
87
+ - [`@mulmobridge/line-works`](https://www.npmjs.com/package/@mulmobridge/line-works) — LINE Works (enterprise LINE)
88
+ - [`@mulmobridge/mastodon`](https://www.npmjs.com/package/@mulmobridge/mastodon) — Mastodon DMs + mentions
89
+ - [`@mulmobridge/matrix`](https://www.npmjs.com/package/@mulmobridge/matrix) — Matrix / Element
90
+ - [`@mulmobridge/mattermost`](https://www.npmjs.com/package/@mulmobridge/mattermost) — Mattermost
91
+ - [`@mulmobridge/messenger`](https://www.npmjs.com/package/@mulmobridge/messenger) — Facebook Messenger via MulmoBridge relay
92
+ - [`@mulmobridge/nostr`](https://www.npmjs.com/package/@mulmobridge/nostr) — Nostr NIP-04 encrypted DMs
93
+ - [`@mulmobridge/rocketchat`](https://www.npmjs.com/package/@mulmobridge/rocketchat) — Rocket.Chat
94
+ - [`@mulmobridge/signal`](https://www.npmjs.com/package/@mulmobridge/signal) — Signal via signal-cli-rest-api
95
+ - [`@mulmobridge/slack`](https://www.npmjs.com/package/@mulmobridge/slack) — Slack Socket Mode
96
+ - [`@mulmobridge/teams`](https://www.npmjs.com/package/@mulmobridge/teams) — Microsoft Teams via Bot Framework
97
+ - [`@mulmobridge/telegram`](https://www.npmjs.com/package/@mulmobridge/telegram) — Telegram bot ← **this package**
98
+ - [`@mulmobridge/twilio-sms`](https://www.npmjs.com/package/@mulmobridge/twilio-sms) — SMS via Twilio Programmable Messaging
99
+ - [`@mulmobridge/viber`](https://www.npmjs.com/package/@mulmobridge/viber) — Viber Public Account bots
100
+ - [`@mulmobridge/webhook`](https://www.npmjs.com/package/@mulmobridge/webhook) — generic HTTP webhook bridge
101
+ - [`@mulmobridge/whatsapp`](https://www.npmjs.com/package/@mulmobridge/whatsapp) — WhatsApp Cloud API via MulmoBridge relay
102
+ - [`@mulmobridge/xmpp`](https://www.npmjs.com/package/@mulmobridge/xmpp) — XMPP / Jabber
103
+ - [`@mulmobridge/zulip`](https://www.npmjs.com/package/@mulmobridge/zulip) — Zulip
56
104
 
57
105
  ## License
58
106
 
package/dist/api.js CHANGED
@@ -74,9 +74,7 @@ export function createTelegramApi(opts) {
74
74
  // authoritative). Fall back to extension-based inference only
75
75
  // when the caller passed the generic default.
76
76
  const ext = getFileBody.result.file_path.split(".").pop() ?? "";
77
- const mediaType = fallbackMime !== "application/octet-stream"
78
- ? fallbackMime
79
- : mimeFromExtension(ext, fallbackMime);
77
+ const mediaType = fallbackMime !== "application/octet-stream" ? fallbackMime : mimeFromExtension(ext, fallbackMime);
80
78
  return buildDataUrl(mediaType, b64);
81
79
  },
82
80
  };
package/dist/index.js CHANGED
@@ -71,9 +71,7 @@ function delay(ms) {
71
71
  async function main() {
72
72
  const { botToken, allowlist, pollTimeoutSec } = readEnv();
73
73
  console.log("MulmoClaude Telegram bridge");
74
- console.log(`Allowlist: ${allowlist.size() > 0
75
- ? allowlist.snapshot().join(", ")
76
- : "(empty — all chats will be denied)"}`);
74
+ console.log(`Allowlist: ${allowlist.size() > 0 ? allowlist.snapshot().join(", ") : "(empty — all chats will be denied)"}`);
77
75
  const api = createTelegramApi({ botToken });
78
76
  const client = createBridgeClient({ transportId: TRANSPORT_ID });
79
77
  const router = createMessageRouter({
@@ -87,9 +85,7 @@ async function main() {
87
85
  });
88
86
  // Server → Telegram async push (Phase B of #268).
89
87
  client.onPush((ev) => {
90
- router
91
- .handlePush(ev)
92
- .catch((err) => console.error(`[telegram] handlePush failed: ${String(err)}`));
88
+ router.handlePush(ev).catch((err) => console.error(`[telegram] handlePush failed: ${String(err)}`));
93
89
  });
94
90
  const abortController = new AbortController();
95
91
  process.on("SIGINT", () => {
package/dist/router.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Telegram API, no real MulmoClaude socket, no env reads). The
4
4
  // entrypoint in index.ts is then just: read env → wire real deps →
5
5
  // drive the polling loop.
6
- import { parseDataUrl, } from "@mulmobridge/client";
6
+ import { parseDataUrl } from "@mulmobridge/client";
7
7
  // Telegram caps a single message at 4096 chars. We split long
8
8
  // replies naively; pretty formatting (preserve markdown, break on
9
9
  // sentence boundaries) is a follow-up.
@@ -127,8 +127,7 @@ export function createMessageRouter(deps) {
127
127
  async function handleAllowed(msg) {
128
128
  const chatId = msg.chat.id;
129
129
  const text = msg.text ?? msg.caption ?? "";
130
- const hasMedia = (Array.isArray(msg.photo) && msg.photo.length > 0) ||
131
- msg.document !== undefined;
130
+ const hasMedia = (Array.isArray(msg.photo) && msg.photo.length > 0) || msg.document !== undefined;
132
131
  if (text.trim().length === 0 && !hasMedia)
133
132
  return;
134
133
  const mediaLabel = buildMediaLabel(msg);
@@ -137,9 +136,7 @@ export function createMessageRouter(deps) {
137
136
  // Attachment-only message where download failed: bail out instead
138
137
  // of sending a query without the actual file.
139
138
  if (failed && text.trim().length === 0) {
140
- await api
141
- .sendMessage(chatId, "Sorry, I could not download the attachment. Please try again.")
142
- .catch(() => { });
139
+ await api.sendMessage(chatId, "Sorry, I could not download the attachment. Please try again.").catch(() => { });
143
140
  return;
144
141
  }
145
142
  // Surface the attachment failure so the user knows the file was lost.
@@ -156,9 +153,7 @@ export function createMessageRouter(deps) {
156
153
  // to catch any trailing chunks. If no streaming happened,
157
154
  // send the full reply as a new message.
158
155
  if (sentMsgId !== null && ack.ok) {
159
- await api
160
- .editMessageText(chatId, sentMsgId, ack.reply ?? "")
161
- .catch(() => { });
156
+ await api.editMessageText(chatId, sentMsgId, ack.reply ?? "").catch(() => { });
162
157
  }
163
158
  else if (sentMsgId === null) {
164
159
  await sendReply(chatId, ack);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulmobridge/telegram",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "Telegram bridge for MulmoBridge — connect a Telegram bot to MulmoClaude",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,12 +30,12 @@
30
30
  "license": "MIT",
31
31
  "author": "Receptron Team",
32
32
  "dependencies": {
33
- "@mulmobridge/client": "^0.1.0",
34
- "@mulmobridge/protocol": "^0.1.0",
33
+ "@mulmobridge/client": "^1.0.0",
34
+ "@mulmobridge/protocol": "^1.0.0",
35
35
  "dotenv": "^17.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/node": "^25.6.0",
38
+ "@types/node": "^26.1.1",
39
39
  "typescript": "^6.0.3"
40
40
  }
41
41
  }