@msgly/line
v0.2.2
Published
LINE Messaging API adapter for Msgly
Downloads
61
Maintainers
Readme
@msgly/line
LINE Messaging API adapter for Msgly. Send and receive LINE messages through the unified hub — text, image, video, audio, location, buttons, and quick replies. Supports LINE's free reply-token sends. Zero classes, runs in Node, Next.js, and Edge runtimes.
Install
npm install @msgly/core @msgly/lineQuick start
import express from 'express';
import { createHub } from '@msgly/core';
import { createLineAdapter } from '@msgly/line';
const hub = createHub();
hub.register(
createLineAdapter({
channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN!,
channelSecret: process.env.LINE_CHANNEL_SECRET!,
}),
);
await hub.connect({ throwOnFailure: true });
hub.on('message', async (msg) => {
if (msg.channel === 'line' && msg.content.type === 'text') {
// Use the free reply token when available
await hub.send({
channel: 'line',
account: msg.account,
contact: msg.contact,
content: { type: 'text', text: `You said: ${msg.content.text}` },
metadata: { replyToken: msg.metadata?.replyToken },
});
}
});
const app = express();
app.use(express.json({ verify: (req, _r, buf) => ((req as any).rawBody = new Uint8Array(buf)) }));
const handlers = hub.createWebhookHandler();
app.get('/webhook/:channel', handlers.get);
app.post('/webhook/:channel', handlers.post);
app.listen(3000);Config
interface LineConfig {
/** Long-lived channel access token from the Messaging API tab. */
channelAccessToken: string;
/** Channel secret from Basic settings — used for webhook signature verification. */
channelSecret: string;
/** Override for tests. Defaults to https://api.line.me */
apiBase?: string;
/** Override for tests. Defaults to https://api-data.line.me (media endpoints). */
dataApiBase?: string;
}Setup (10 minutes)
- Sign up at developers.line.biz. Create a Provider, then a Messaging API channel inside it.
- Channel secret. Open the channel → Basic settings → copy Channel secret → set as
LINE_CHANNEL_SECRET. - Channel access token. Messaging API tab → under "Channel access token (long-lived)" click Issue → copy → set as
LINE_CHANNEL_ACCESS_TOKEN. - Webhook URL. On the Messaging API tab:
- Webhook URL:
<PUBLIC_URL>/webhook/line - Toggle Use webhook to ON
- Click Verify — should succeed
- Webhook URL:
- Disable LINE's built-in replies. Same tab:
- "Auto-reply messages": OFF (otherwise LINE answers before your bot)
- "Greeting messages": OFF (optional)
- Add the bot to a chat. Find the bot's QR code on the Messaging API tab → scan with the LINE app → add as friend → message it.
Capabilities
| Feature | Supported | | ------------- | --------- | | text | ✓ | | image | ✓ | | video | ✓ | | audio | ✓ | | file | — | | location | ✓ | | buttons | ✓ | | quick replies | ✓ (max 13, 20-char labels) | | templates | — | | reactions | — | | typing | — |
Reply tokens (free vs push)
LINE's pricing model:
- Reply API (uses a
replyTokenfrom the inbound event) — free, doesn't count against your monthly quota. - Push API (no reply token) — counts against your push quota.
Reply tokens are single-use and expire about 1 minute after the inbound message. Use them when responding immediately:
hub.on('message', async (msg) => {
if (msg.channel === 'line' && msg.content.type === 'text') {
await hub.send({
channel: 'line',
account: msg.account,
contact: msg.contact,
content: { type: 'text', text: 'thanks!' },
metadata: { replyToken: msg.metadata?.replyToken }, // ← free reply
});
}
});Send without replyToken and the adapter falls back to push automatically.
Sending examples
Image
await hub.send({
channel: 'line',
account, contact,
content: {
type: 'image',
mediaRef: { kind: 'url', value: 'https://example.com/cat.jpg' },
},
});LINE requires media URLs to be public HTTPS with no auth.
Quick replies
await hub.send({
channel: 'line',
account, contact,
content: {
type: 'interactive',
text: 'Pick one:',
buttons: [
{ id: 'a', label: 'Option A' },
{ id: 'b', label: 'Option B' },
],
},
});User tap → you receive a text message whose content.text matches the button id.
Common pitfalls
- Bot replies but user doesn't see it: in the LINE Developers Console, "Auto-reply messages" must be OFF. It overrides your bot's responses.
Invalid reply token: the token expired (>1 min) or was already used. Fall back to push by omittingmetadata.replyToken.InvalidSignature:LINE_CHANNEL_SECRETis wrong, or your Express app isn't capturing the raw body. Theverifycallback inexpress.json()is essential.- Webhook verify fails in console: server must be running and reachable at the public URL when you click Verify.
Documentation
Full setup walkthrough and multi-channel usage: https://github.com/AyushJain070401/msgly
License
MIT
