@mulmobridge/line 0.1.0 → 0.1.1
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 +56 -2
- package/dist/index.js +14 -23
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,11 @@ LINE bridge for [MulmoClaude](https://github.com/receptron/mulmoclaude). Uses we
|
|
|
14
14
|
|
|
15
15
|
### 2. Set up ngrok (for development)
|
|
16
16
|
|
|
17
|
+
[ngrok](https://ngrok.com) is a tunneling tool that exposes a local port to the internet so LINE's webhooks can reach your machine.
|
|
18
|
+
|
|
17
19
|
```bash
|
|
20
|
+
brew install ngrok
|
|
21
|
+
ngrok config add-authtoken <your-token> # from ngrok dashboard
|
|
18
22
|
ngrok http 3002
|
|
19
23
|
# Copy the https://xxxx.ngrok-free.app URL
|
|
20
24
|
```
|
|
@@ -22,9 +26,9 @@ ngrok http 3002
|
|
|
22
26
|
### 3. Configure the webhook
|
|
23
27
|
|
|
24
28
|
In the LINE Developers Console → Messaging API tab:
|
|
25
|
-
- **Webhook URL**: `https://xxxx.ngrok-free.app/webhook`
|
|
29
|
+
- **Webhook URL**: `https://xxxx.ngrok-free.app/webhook` — the trailing `/webhook` is **required** (without it you get 404)
|
|
26
30
|
- **Use webhook**: enabled
|
|
27
|
-
- **Auto-reply messages**: disabled (
|
|
31
|
+
- **Auto-reply messages**: disabled (LINE Official Account settings → Auto-reply messages → OFF, otherwise you get double replies)
|
|
28
32
|
|
|
29
33
|
### 4. Run the bridge
|
|
30
34
|
|
|
@@ -55,12 +59,62 @@ Scan the QR code in the LINE Developers Console → Messaging API tab. Send a me
|
|
|
55
59
|
| `LINE_BRIDGE_PORT` | No | Webhook port (default: 3002) |
|
|
56
60
|
| `MULMOCLAUDE_API_URL` | No | Default `http://localhost:3001` |
|
|
57
61
|
| `MULMOCLAUDE_AUTH_TOKEN` | No | Bearer token |
|
|
62
|
+
| `LINE_BRIDGE_DEFAULT_ROLE` | No | Role id to seed new bridge sessions with (e.g. `coder`, `general`). Applied ONLY when a line 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. |
|
|
63
|
+
| `BRIDGE_DEFAULT_ROLE` | No | Same as above but shared across every bridge. Transport-specific `LINE_BRIDGE_DEFAULT_ROLE` wins when both are set. |
|
|
58
64
|
|
|
59
65
|
## Notes
|
|
60
66
|
|
|
61
67
|
- LINE reply tokens expire in **1 minute**. Since Claude responses can take longer, the bridge uses **push messages** instead of reply messages. This requires your bot to be a verified/certified account for push to work with all users, OR the user must have added the bot as a friend first.
|
|
62
68
|
- LINE limits messages to 5 per push call and ~5000 chars per message. Long replies are automatically chunked.
|
|
63
69
|
|
|
70
|
+
## Detailed Setup Guide
|
|
71
|
+
|
|
72
|
+
For step-by-step instructions with troubleshooting:
|
|
73
|
+
|
|
74
|
+
- [English](https://github.com/receptron/mulmoclaude/blob/main/docs/message_apps/line/README.md)
|
|
75
|
+
- [Japanese](https://github.com/receptron/mulmoclaude/blob/main/docs/message_apps/line/README.ja.md)
|
|
76
|
+
|
|
77
|
+
## Ecosystem
|
|
78
|
+
|
|
79
|
+
Part of the [`@mulmobridge/*`](https://www.npmjs.com/~mulmobridge) package family.
|
|
80
|
+
|
|
81
|
+
**Shared libraries:**
|
|
82
|
+
|
|
83
|
+
- [`@mulmobridge/client`](https://www.npmjs.com/package/@mulmobridge/client) — socket.io client library used by every bridge below
|
|
84
|
+
- [`@mulmobridge/protocol`](https://www.npmjs.com/package/@mulmobridge/protocol) — wire types and constants
|
|
85
|
+
- [`@mulmobridge/chat-service`](https://www.npmjs.com/package/@mulmobridge/chat-service) — server-side relay + session store
|
|
86
|
+
- [`@mulmobridge/relay`](https://www.npmjs.com/package/@mulmobridge/relay) — Cloudflare Workers webhook proxy
|
|
87
|
+
- [`@mulmobridge/mock-server`](https://www.npmjs.com/package/@mulmobridge/mock-server) — mock server for local bridge development
|
|
88
|
+
|
|
89
|
+
**Bridges** (one npm package per platform):
|
|
90
|
+
|
|
91
|
+
- [`@mulmobridge/bluesky`](https://www.npmjs.com/package/@mulmobridge/bluesky) — Bluesky DMs over atproto
|
|
92
|
+
- [`@mulmobridge/chatwork`](https://www.npmjs.com/package/@mulmobridge/chatwork) — Chatwork (Japanese business chat)
|
|
93
|
+
- [`@mulmobridge/cli`](https://www.npmjs.com/package/@mulmobridge/cli) — interactive terminal bridge
|
|
94
|
+
- [`@mulmobridge/discord`](https://www.npmjs.com/package/@mulmobridge/discord) — Discord bot via Gateway
|
|
95
|
+
- [`@mulmobridge/email`](https://www.npmjs.com/package/@mulmobridge/email) — IMAP poll + SMTP reply, threading preserved
|
|
96
|
+
- [`@mulmobridge/google-chat`](https://www.npmjs.com/package/@mulmobridge/google-chat) — Google Chat via MulmoBridge relay
|
|
97
|
+
- [`@mulmobridge/irc`](https://www.npmjs.com/package/@mulmobridge/irc) — IRC (Libera, Freenode, custom)
|
|
98
|
+
- [`@mulmobridge/line`](https://www.npmjs.com/package/@mulmobridge/line) — LINE Messaging API via MulmoBridge relay ← **this package**
|
|
99
|
+
- [`@mulmobridge/line-works`](https://www.npmjs.com/package/@mulmobridge/line-works) — LINE Works (enterprise LINE)
|
|
100
|
+
- [`@mulmobridge/mastodon`](https://www.npmjs.com/package/@mulmobridge/mastodon) — Mastodon DMs + mentions
|
|
101
|
+
- [`@mulmobridge/matrix`](https://www.npmjs.com/package/@mulmobridge/matrix) — Matrix / Element
|
|
102
|
+
- [`@mulmobridge/mattermost`](https://www.npmjs.com/package/@mulmobridge/mattermost) — Mattermost
|
|
103
|
+
- [`@mulmobridge/messenger`](https://www.npmjs.com/package/@mulmobridge/messenger) — Facebook Messenger via MulmoBridge relay
|
|
104
|
+
- [`@mulmobridge/nostr`](https://www.npmjs.com/package/@mulmobridge/nostr) — Nostr NIP-04 encrypted DMs
|
|
105
|
+
- [`@mulmobridge/rocketchat`](https://www.npmjs.com/package/@mulmobridge/rocketchat) — Rocket.Chat
|
|
106
|
+
- [`@mulmobridge/signal`](https://www.npmjs.com/package/@mulmobridge/signal) — Signal via signal-cli-rest-api
|
|
107
|
+
- [`@mulmobridge/slack`](https://www.npmjs.com/package/@mulmobridge/slack) — Slack Socket Mode
|
|
108
|
+
- [`@mulmobridge/teams`](https://www.npmjs.com/package/@mulmobridge/teams) — Microsoft Teams via Bot Framework
|
|
109
|
+
- [`@mulmobridge/telegram`](https://www.npmjs.com/package/@mulmobridge/telegram) — Telegram bot
|
|
110
|
+
- [`@mulmobridge/twilio-sms`](https://www.npmjs.com/package/@mulmobridge/twilio-sms) — SMS via Twilio Programmable Messaging
|
|
111
|
+
- [`@mulmobridge/viber`](https://www.npmjs.com/package/@mulmobridge/viber) — Viber Public Account bots
|
|
112
|
+
- [`@mulmobridge/webhook`](https://www.npmjs.com/package/@mulmobridge/webhook) — generic HTTP webhook bridge
|
|
113
|
+
- [`@mulmobridge/whatsapp`](https://www.npmjs.com/package/@mulmobridge/whatsapp) — WhatsApp Cloud API via MulmoBridge relay
|
|
114
|
+
- [`@mulmobridge/xmpp`](https://www.npmjs.com/package/@mulmobridge/xmpp) — XMPP / Jabber
|
|
115
|
+
- [`@mulmobridge/zulip`](https://www.npmjs.com/package/@mulmobridge/zulip) — Zulip
|
|
116
|
+
|
|
117
|
+
|
|
64
118
|
## License
|
|
65
119
|
|
|
66
120
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -15,36 +15,39 @@
|
|
|
15
15
|
import "dotenv/config";
|
|
16
16
|
import crypto from "crypto";
|
|
17
17
|
import express from "express";
|
|
18
|
-
import { createBridgeClient } from "@mulmobridge/client";
|
|
18
|
+
import { createBridgeClient, chunkText } from "@mulmobridge/client";
|
|
19
19
|
const TRANSPORT_ID = "line";
|
|
20
20
|
const PORT = Number(process.env.LINE_BRIDGE_PORT) || 3002;
|
|
21
21
|
const channelSecret = process.env.LINE_CHANNEL_SECRET;
|
|
22
22
|
const channelAccessToken = process.env.LINE_CHANNEL_ACCESS_TOKEN;
|
|
23
23
|
if (!channelSecret || !channelAccessToken) {
|
|
24
|
-
console.error("LINE_CHANNEL_SECRET and LINE_CHANNEL_ACCESS_TOKEN are required.\n" +
|
|
25
|
-
"See README for setup instructions.");
|
|
24
|
+
console.error("LINE_CHANNEL_SECRET and LINE_CHANNEL_ACCESS_TOKEN are required.\n" + "See README for setup instructions.");
|
|
26
25
|
process.exit(1);
|
|
27
26
|
}
|
|
28
27
|
const client = createBridgeClient({ transportId: TRANSPORT_ID });
|
|
29
|
-
client.onPush((
|
|
30
|
-
pushMessage(
|
|
28
|
+
client.onPush((pushEvent) => {
|
|
29
|
+
pushMessage(pushEvent.chatId, pushEvent.message).catch((err) => console.error(`[line] push send failed: ${err}`));
|
|
31
30
|
});
|
|
32
31
|
// ── LINE API helpers ────────────────────────────────────────────
|
|
33
32
|
async function pushMessage(userId, text) {
|
|
34
|
-
const messages = chunkText(text).map((
|
|
33
|
+
const messages = chunkText(text, 5000).map((messageText) => ({
|
|
34
|
+
type: "text",
|
|
35
|
+
text: messageText,
|
|
36
|
+
}));
|
|
35
37
|
// LINE allows max 5 messages per push
|
|
36
38
|
for (let i = 0; i < messages.length; i += 5) {
|
|
37
39
|
try {
|
|
40
|
+
const requestBody = {
|
|
41
|
+
to: userId,
|
|
42
|
+
messages: messages.slice(i, i + 5),
|
|
43
|
+
};
|
|
38
44
|
const res = await fetch("https://api.line.me/v2/bot/message/push", {
|
|
39
45
|
method: "POST",
|
|
40
46
|
headers: {
|
|
41
47
|
"Content-Type": "application/json",
|
|
42
48
|
Authorization: `Bearer ${channelAccessToken}`,
|
|
43
49
|
},
|
|
44
|
-
body: JSON.stringify(
|
|
45
|
-
to: userId,
|
|
46
|
-
messages: messages.slice(i, i + 5),
|
|
47
|
-
}),
|
|
50
|
+
body: JSON.stringify(requestBody),
|
|
48
51
|
signal: AbortSignal.timeout(30_000),
|
|
49
52
|
});
|
|
50
53
|
if (!res.ok) {
|
|
@@ -57,21 +60,9 @@ async function pushMessage(userId, text) {
|
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
|
-
function chunkText(text, max = 5000) {
|
|
61
|
-
if (text.length === 0)
|
|
62
|
-
return ["(empty reply)"];
|
|
63
|
-
const chunks = [];
|
|
64
|
-
for (let i = 0; i < text.length; i += max) {
|
|
65
|
-
chunks.push(text.slice(i, i + max));
|
|
66
|
-
}
|
|
67
|
-
return chunks;
|
|
68
|
-
}
|
|
69
63
|
// ── Signature verification ──────────────────────────────────────
|
|
70
64
|
function verifySignature(body, signature) {
|
|
71
|
-
const expected = crypto
|
|
72
|
-
.createHmac("SHA256", channelSecret)
|
|
73
|
-
.update(body)
|
|
74
|
-
.digest("base64");
|
|
65
|
+
const expected = crypto.createHmac("SHA256", channelSecret).update(body).digest("base64");
|
|
75
66
|
if (expected.length !== signature.length)
|
|
76
67
|
return false;
|
|
77
68
|
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mulmobridge/line",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "LINE bridge for MulmoBridge — connect a LINE bot to MulmoClaude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Receptron Team",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@mulmobridge/client": "^0.1.
|
|
25
|
-
"@mulmobridge/protocol": "^0.1.
|
|
24
|
+
"@mulmobridge/client": "^0.1.4",
|
|
25
|
+
"@mulmobridge/protocol": "^0.1.4",
|
|
26
26
|
"dotenv": "^17.0.0",
|
|
27
27
|
"express": "^5.1.0"
|
|
28
28
|
},
|