@mulmobridge/discord 0.1.0 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +61 -0
  2. package/dist/index.js +15 -15
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -47,6 +47,67 @@ npx @mulmobridge/discord
47
47
  | `DISCORD_ALLOWED_CHANNELS` | No | CSV of channel IDs to restrict (empty = all) |
48
48
  | `MULMOCLAUDE_API_URL` | No | Default `http://localhost:3001` |
49
49
  | `MULMOCLAUDE_AUTH_TOKEN` | No | Bearer token (auto-read from workspace) |
50
+ | `DISCORD_BRIDGE_DEFAULT_ROLE` | No | Role id to seed new bridge sessions with (e.g. `coder`, `general`). Applied ONLY when a discord session first appears — once the user switches role via `/role <id>` the session's own role wins. Unknown role ids silently fall back to the server's default with a warn log. |
51
+ | `BRIDGE_DEFAULT_ROLE` | No | Same as above but shared across every bridge. Transport-specific `DISCORD_BRIDGE_DEFAULT_ROLE` wins when both are set. |
52
+
53
+ ### Auth token persistence across server restarts
54
+
55
+ 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.
56
+
57
+ **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.
58
+
59
+ ```bash
60
+ # Server (one-time setup — same value across restarts)
61
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string yarn dev
62
+
63
+ # Bridge (separate process / machine — same value)
64
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string \
65
+ <bridge-specific-envs> \
66
+ npx <this-package>@latest
67
+ ```
68
+
69
+ Recommended: at least 32 characters of random data (the server logs a warning at startup for shorter values).
70
+
71
+ ## Ecosystem
72
+
73
+ Part of the [`@mulmobridge/*`](https://www.npmjs.com/~mulmobridge) package family.
74
+
75
+ **Shared libraries:**
76
+
77
+ - [`@mulmobridge/client`](https://www.npmjs.com/package/@mulmobridge/client) — socket.io client library used by every bridge below
78
+ - [`@mulmobridge/protocol`](https://www.npmjs.com/package/@mulmobridge/protocol) — wire types and constants
79
+ - [`@mulmobridge/chat-service`](https://www.npmjs.com/package/@mulmobridge/chat-service) — server-side relay + session store
80
+ - [`@mulmobridge/relay`](https://www.npmjs.com/package/@mulmobridge/relay) — Cloudflare Workers webhook proxy
81
+ - [`@mulmobridge/mock-server`](https://www.npmjs.com/package/@mulmobridge/mock-server) — mock server for local bridge development
82
+
83
+ **Bridges** (one npm package per platform):
84
+
85
+ - [`@mulmobridge/bluesky`](https://www.npmjs.com/package/@mulmobridge/bluesky) — Bluesky DMs over atproto
86
+ - [`@mulmobridge/chatwork`](https://www.npmjs.com/package/@mulmobridge/chatwork) — Chatwork (Japanese business chat)
87
+ - [`@mulmobridge/cli`](https://www.npmjs.com/package/@mulmobridge/cli) — interactive terminal bridge
88
+ - [`@mulmobridge/discord`](https://www.npmjs.com/package/@mulmobridge/discord) — Discord bot via Gateway ← **this package**
89
+ - [`@mulmobridge/email`](https://www.npmjs.com/package/@mulmobridge/email) — IMAP poll + SMTP reply, threading preserved
90
+ - [`@mulmobridge/google-chat`](https://www.npmjs.com/package/@mulmobridge/google-chat) — Google Chat via MulmoBridge relay
91
+ - [`@mulmobridge/irc`](https://www.npmjs.com/package/@mulmobridge/irc) — IRC (Libera, Freenode, custom)
92
+ - [`@mulmobridge/line`](https://www.npmjs.com/package/@mulmobridge/line) — LINE Messaging API via MulmoBridge relay
93
+ - [`@mulmobridge/line-works`](https://www.npmjs.com/package/@mulmobridge/line-works) — LINE Works (enterprise LINE)
94
+ - [`@mulmobridge/mastodon`](https://www.npmjs.com/package/@mulmobridge/mastodon) — Mastodon DMs + mentions
95
+ - [`@mulmobridge/matrix`](https://www.npmjs.com/package/@mulmobridge/matrix) — Matrix / Element
96
+ - [`@mulmobridge/mattermost`](https://www.npmjs.com/package/@mulmobridge/mattermost) — Mattermost
97
+ - [`@mulmobridge/messenger`](https://www.npmjs.com/package/@mulmobridge/messenger) — Facebook Messenger via MulmoBridge relay
98
+ - [`@mulmobridge/nostr`](https://www.npmjs.com/package/@mulmobridge/nostr) — Nostr NIP-04 encrypted DMs
99
+ - [`@mulmobridge/rocketchat`](https://www.npmjs.com/package/@mulmobridge/rocketchat) — Rocket.Chat
100
+ - [`@mulmobridge/signal`](https://www.npmjs.com/package/@mulmobridge/signal) — Signal via signal-cli-rest-api
101
+ - [`@mulmobridge/slack`](https://www.npmjs.com/package/@mulmobridge/slack) — Slack Socket Mode
102
+ - [`@mulmobridge/teams`](https://www.npmjs.com/package/@mulmobridge/teams) — Microsoft Teams via Bot Framework
103
+ - [`@mulmobridge/telegram`](https://www.npmjs.com/package/@mulmobridge/telegram) — Telegram bot
104
+ - [`@mulmobridge/twilio-sms`](https://www.npmjs.com/package/@mulmobridge/twilio-sms) — SMS via Twilio Programmable Messaging
105
+ - [`@mulmobridge/viber`](https://www.npmjs.com/package/@mulmobridge/viber) — Viber Public Account bots
106
+ - [`@mulmobridge/webhook`](https://www.npmjs.com/package/@mulmobridge/webhook) — generic HTTP webhook bridge
107
+ - [`@mulmobridge/whatsapp`](https://www.npmjs.com/package/@mulmobridge/whatsapp) — WhatsApp Cloud API via MulmoBridge relay
108
+ - [`@mulmobridge/xmpp`](https://www.npmjs.com/package/@mulmobridge/xmpp) — XMPP / Jabber
109
+ - [`@mulmobridge/zulip`](https://www.npmjs.com/package/@mulmobridge/zulip) — Zulip
110
+
50
111
 
51
112
  ## License
52
113
 
package/dist/index.js CHANGED
@@ -20,41 +20,41 @@ if (!token) {
20
20
  }
21
21
  const allowedChannels = new Set((process.env.DISCORD_ALLOWED_CHANNELS ?? "")
22
22
  .split(",")
23
- .map((s) => s.trim())
23
+ .map((channelId) => channelId.trim())
24
24
  .filter(Boolean));
25
25
  const allowAll = allowedChannels.size === 0;
26
26
  const discord = new Client({
27
- intents: [
28
- GatewayIntentBits.Guilds,
29
- GatewayIntentBits.GuildMessages,
30
- GatewayIntentBits.DirectMessages,
31
- GatewayIntentBits.MessageContent,
32
- ],
27
+ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent],
33
28
  // Partials.Channel is required to receive DM messageCreate events —
34
29
  // DM channels are not cached by default in discord.js v14.
35
30
  partials: [Partials.Channel],
36
31
  });
37
32
  const mulmo = createBridgeClient({ transportId: TRANSPORT_ID });
38
- mulmo.onPush(async (ev) => {
33
+ mulmo.onPush((pushEvent) => {
34
+ onPushEvent(pushEvent).catch((err) => console.error(`[discord] push handler error: ${err}`));
35
+ });
36
+ async function onPushEvent(pushEvent) {
39
37
  try {
40
- const channel = discord.channels.cache.get(ev.chatId) ??
41
- (await discord.channels.fetch(ev.chatId).catch(() => null));
38
+ const channel = discord.channels.cache.get(pushEvent.chatId) ?? (await discord.channels.fetch(pushEvent.chatId).catch(() => null));
42
39
  if (channel?.isTextBased() && "send" in channel) {
43
40
  const sendable = channel;
44
- await sendChunkedToSendable(sendable, ev.message);
41
+ await sendChunkedToSendable(sendable, pushEvent.message);
45
42
  }
46
43
  else {
47
- console.warn(`[discord] push: channel ${ev.chatId} not found or not text-based`);
44
+ console.warn(`[discord] push: channel ${pushEvent.chatId} not found or not text-based`);
48
45
  }
49
46
  }
50
47
  catch (err) {
51
48
  console.error(`[discord] push send failed: ${err}`);
52
49
  }
50
+ }
51
+ discord.on("messageCreate", (msg) => {
52
+ onMessageCreate(msg).catch((err) => console.error(`[discord] messageCreate handler error: ${err}`));
53
53
  });
54
- discord.on("messageCreate", async (msg) => {
54
+ async function onMessageCreate(msg) {
55
55
  if (msg.author.bot)
56
56
  return;
57
- const channelId = msg.channelId;
57
+ const { channelId } = msg;
58
58
  const text = msg.content.trim();
59
59
  if (!text)
60
60
  return;
@@ -74,7 +74,7 @@ discord.on("messageCreate", async (msg) => {
74
74
  catch (err) {
75
75
  console.error(`[discord] message handling failed: ${err}`);
76
76
  }
77
- });
77
+ }
78
78
  async function sendChunked(msg, text) {
79
79
  if (text.length === 0) {
80
80
  await msg.reply("(empty reply)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulmobridge/discord",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Discord bridge for MulmoBridge — connect a Discord bot to MulmoClaude",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,13 +21,13 @@
21
21
  "license": "MIT",
22
22
  "author": "Receptron Team",
23
23
  "dependencies": {
24
- "@mulmobridge/client": "^0.1.0",
25
- "@mulmobridge/protocol": "^0.1.0",
26
- "discord.js": "^14.26.3",
24
+ "@mulmobridge/client": "^0.1.4",
25
+ "@mulmobridge/protocol": "^0.1.4",
26
+ "discord.js": "^14.27.0",
27
27
  "dotenv": "^17.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "^25.6.0",
30
+ "@types/node": "^26.1.1",
31
31
  "typescript": "^6.0.3"
32
32
  }
33
33
  }