@mulmobridge/line 1.0.2 → 1.0.4
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/index.js +3 -3
- package/dist/parse.d.ts +3 -1
- package/dist/parse.js +36 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ async function downloadLineImage(messageId) {
|
|
|
51
51
|
// serves the original sender format (PNG, GIF, …). Trust the
|
|
52
52
|
// Content-Type header so the photo-EXIF hook (#1222 PR-A) can
|
|
53
53
|
// tell HEIC apart from JPEG and surface the right sidecar.
|
|
54
|
-
const mimeType = res.headers.get("content-type")?.split(";")[0]
|
|
54
|
+
const mimeType = res.headers.get("content-type")?.split(";")[0]?.trim() || "image/jpeg";
|
|
55
55
|
const arrayBuffer = await res.arrayBuffer();
|
|
56
56
|
return { bytes: Buffer.from(arrayBuffer), mimeType };
|
|
57
57
|
}
|
|
@@ -95,8 +95,8 @@ async function pushMessage(userId, text) {
|
|
|
95
95
|
const app = createWebhookApp();
|
|
96
96
|
const webhookRateLimit = createWebhookRateLimit();
|
|
97
97
|
app.post("/webhook", webhookRateLimit, async (req, res) => {
|
|
98
|
-
const signature = req.headers["x-line-signature"];
|
|
99
|
-
const bodyStr = req.body;
|
|
98
|
+
const signature = typeof req.headers["x-line-signature"] === "string" ? req.headers["x-line-signature"] : "";
|
|
99
|
+
const bodyStr = typeof req.body === "string" ? req.body : "";
|
|
100
100
|
if (!signature || !verifyHmacSignature(bodyStr, signature, channelSecret)) {
|
|
101
101
|
res.status(401).send("Invalid signature");
|
|
102
102
|
return;
|
package/dist/parse.d.ts
CHANGED
|
@@ -41,5 +41,7 @@ export type IncomingLineMessage = {
|
|
|
41
41
|
* unsupported media). Pure — no side effects, no allowlist check.
|
|
42
42
|
*/
|
|
43
43
|
export declare function extractIncomingLineMessage(event: LineEvent): IncomingLineMessage | null;
|
|
44
|
-
/** Best-effort JSON parse for the webhook body — null on malformed input.
|
|
44
|
+
/** Best-effort JSON parse for the webhook body — null on malformed input.
|
|
45
|
+
* Events that don't match `LineEvent` are dropped rather than handed on as if
|
|
46
|
+
* they did: a `null` element used to throw inside the delivery loop. */
|
|
45
47
|
export declare function parseLineWebhookBody(raw: string): LineWebhookBody | null;
|
package/dist/parse.js
CHANGED
|
@@ -24,14 +24,46 @@ export function extractIncomingLineMessage(event) {
|
|
|
24
24
|
}
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
const isOptionalString = (value) => value === undefined || typeof value === "string";
|
|
28
|
+
function isLineEventSource(value) {
|
|
29
|
+
if (typeof value !== "object" || value === null)
|
|
30
|
+
return false;
|
|
31
|
+
if ("userId" in value && !isOptionalString(value.userId))
|
|
32
|
+
return false;
|
|
33
|
+
return !("type" in value) || isOptionalString(value.type);
|
|
34
|
+
}
|
|
35
|
+
function isLineMessage(value) {
|
|
36
|
+
if (typeof value !== "object" || value === null)
|
|
37
|
+
return false;
|
|
38
|
+
if (!("type" in value) || typeof value.type !== "string")
|
|
39
|
+
return false;
|
|
40
|
+
if ("text" in value && !isOptionalString(value.text))
|
|
41
|
+
return false;
|
|
42
|
+
return !("id" in value) || isOptionalString(value.id);
|
|
43
|
+
}
|
|
44
|
+
/** True only when every field `LineEvent` declares is present in the shape it
|
|
45
|
+
* declares. Extra platform fields we don't model are left untouched. */
|
|
46
|
+
function isLineEvent(value) {
|
|
47
|
+
if (typeof value !== "object" || value === null)
|
|
48
|
+
return false;
|
|
49
|
+
if (!("type" in value) || typeof value.type !== "string")
|
|
50
|
+
return false;
|
|
51
|
+
if ("replyToken" in value && !isOptionalString(value.replyToken))
|
|
52
|
+
return false;
|
|
53
|
+
if ("source" in value && value.source !== undefined && !isLineEventSource(value.source))
|
|
54
|
+
return false;
|
|
55
|
+
return !("message" in value) || value.message === undefined || isLineMessage(value.message);
|
|
56
|
+
}
|
|
57
|
+
/** Best-effort JSON parse for the webhook body — null on malformed input.
|
|
58
|
+
* Events that don't match `LineEvent` are dropped rather than handed on as if
|
|
59
|
+
* they did: a `null` element used to throw inside the delivery loop. */
|
|
28
60
|
export function parseLineWebhookBody(raw) {
|
|
29
61
|
try {
|
|
30
62
|
const parsed = JSON.parse(raw);
|
|
31
|
-
if (
|
|
63
|
+
if (typeof parsed !== "object" || parsed === null || !("events" in parsed))
|
|
32
64
|
return null;
|
|
33
|
-
}
|
|
34
|
-
return
|
|
65
|
+
const { events } = parsed;
|
|
66
|
+
return Array.isArray(events) ? { events: events.filter(isLineEvent) } : null;
|
|
35
67
|
}
|
|
36
68
|
catch {
|
|
37
69
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mulmobridge/line",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "LINE bridge for MulmoBridge — connect a LINE bot to MulmoClaude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"author": "Receptron Team",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@mulmobridge/client": "^1.0.
|
|
25
|
+
"@mulmobridge/client": "^1.0.2",
|
|
26
26
|
"@mulmobridge/protocol": "^1.0.1",
|
|
27
27
|
"@mulmobridge/webhook-runtime": "^1.0.3",
|
|
28
28
|
"dotenv": "^17.0.0",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/express": "^5.0.0",
|
|
34
34
|
"@types/node": "^26.1.2",
|
|
35
|
-
"tsx": "^4.23.
|
|
35
|
+
"tsx": "^4.23.4",
|
|
36
36
|
"typescript": "^6.0.3"
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/receptron/mulmoclaude/tree/main/packages/bridges/line#readme",
|