chat-adapter-line 0.0.0 → 0.1.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/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{PermissionError as e,ValidationError as t,extractCard as n,extractFiles as r}from"@chat-adapter/shared";import i from"node:crypto";import{LineBotClient as a}from"@line/bot-sdk";import{BaseFormatConverter as o,ConsoleLogger as s,Message as c,deriveChannelId as l,parseMarkdown as u,stringifyMarkdown as d}from"chat";const f=`line:`,p=(e,t,n)=>`${f}${t}:${e}:${n}`,m=e=>{if(!e.startsWith(f))throw Error(`Invalid LINE thread ID: ${e}`);let t=e.slice(5),n=t.indexOf(`:`);if(n===-1)throw Error(`Invalid LINE thread ID format: ${e}. Expected "line:<channelId>:<sourceType>:<sourceId>"`);let r=t.slice(0,n),i=t.slice(n+1).split(`:`);if(i.length<2)throw Error(`Invalid LINE thread ID format: ${e}. Missing sourceType and sourceId.`);let[a]=i;if(a!==`user`&&a!==`group`&&a!==`room`)throw Error(`Invalid source type in thread ID: ${a}. Must be "user", "group", or "room"`);return{channelId:r,sourceId:i.slice(1).join(`:`),sourceType:a}},h=e=>{try{let{sourceType:t}=m(e);return t===`user`}catch{return!1}},g=/^#{1,6}\s+/gm,_=/\*\*\*(.+?)\*\*\*/g,v=/\*\*(.+?)\*\*/g,y=/\*(.+?)\*/g,b=/__(.+?)__/g,x=/_(.+?)_/g,S=/~~(.+?)~~/g,C=/`(.+?)`/g,w=/```[\s\S]*?```/g,T=/\[([^\]]+)\]\([^)]+\)/g,E=/!\[([^\]]*)\]\([^)]+\)/g,D=/^>\s+/gm,O=/^[\s]*[-*+]\s+/gm,k=/^[\s]*\d+\.\s+/gm,A=/^[-*_]{3,}\s*$/gm,j=/\n{3,}/g,M=e=>e.replaceAll(g,``).replaceAll(_,`$1`).replaceAll(v,`$1`).replaceAll(y,`$1`).replaceAll(b,`$1`).replaceAll(x,`$1`).replaceAll(S,`$1`).replaceAll(C,`$1`).replaceAll(w,e=>e.replaceAll("```",``).trim()).replaceAll(T,`$1`).replaceAll(E,`$1`).replaceAll(D,``).replaceAll(O,``).replaceAll(k,``).replaceAll(A,``).replaceAll(j,`
|
|
2
|
+
|
|
3
|
+
`).trim(),N=new Set([`image`,`video`,`audio`,`file`]),P=(e,t,n)=>{if(!t)return!1;let r=i.createHmac(`SHA256`,n).update(e).digest(`base64`);return i.timingSafeEqual(Buffer.from(t),Buffer.from(r))},F=e=>{let{source:t}=e;return t.type===`user`?t.userId:t.type===`group`?t.groupId:t.roomId},I=async e=>{let t=[];for await(let n of e)t.push(n instanceof Buffer?n:Buffer.from(n));return t.length===1?t[0]:Buffer.concat(t)},L=e=>e.type===`message`&&typeof e.source==`object`&&e.source!==null&&typeof e.source.type==`string`&&typeof e.timestamp==`number`&&typeof e.replyToken==`string`&&typeof e.message==`object`&&e.message!==null&&typeof e.message.type==`string`&&typeof e.message.id==`string`,R=e=>e===`image`?`image/jpeg`:e===`video`?`video/mp4`:e===`audio`?`audio/mp4`:`application/octet-stream`,z=e=>typeof e==`string`?e:e.type===`markdown_text`?e.text:``;var B=class extends o{toAst(e){return u(e)}fromAst(e){return d(e)}renderPostable(e){return M(super.renderPostable(e))}},V=class{name=`line`;userName;chat=null;logger;client;channelSecret;channelId=null;converter=new B;threadCache=new Map;lastTypingTime=new Map;constructor(e){this.client=a.fromChannelAccessToken({channelAccessToken:e.channelAccessToken}),this.channelSecret=e.channelSecret,this.userName=e.userName??`line-bot`,this.logger=e.logger??new s}async initialize(e){this.chat=e,this.logger=e.getLogger(`line`);try{let e=await this.client.getBotInfo();this.channelId=e.userId,this.logger.info(`LINE adapter initialized`,{botId:e.userId})}catch(e){this.logger.error(`Failed to fetch bot info`,{error:e}),this.channelId=`unknown`}}disconnect(){return this.chat=null,Promise.resolve()}channelIdFromThreadId(e){return l(this,e)}encodeThreadId(e){if(!this.channelId)throw new t(`line`,`Channel ID not available. Ensure the adapter is initialized before encoding thread IDs.`);return p(e.sourceType,this.channelId,e.sourceId)}decodeThreadId(e){return m(e)}async handleWebhook(e,t){let n=e.headers.get(`x-line-signature`),r=await e.text();if(!P(r,n,this.channelSecret))return new Response(`Invalid signature`,{status:401});let i;try{i=JSON.parse(r)}catch{return new Response(`Invalid JSON`,{status:400})}if(!this.chat||!i.events)return new Response(`OK`,{status:200});let a=this.channelId??i.destination;for(let e of i.events){if(!L(e)||e.mode!==`active`||e.deliveryContext?.isRedelivery)continue;let n=F(e);if(!n)continue;let r=p(e.source.type,a,n),i=()=>Promise.resolve(this.parseMessage(e));try{this.chat.processMessage(this,r,i,t)}catch(e){this.logger.error(`processMessage failed`,{error:e,threadId:r})}}return new Response(`OK`,{status:200})}parseMessage(e){let n=F(e);if(!this.channelId)throw new t(`line`,`Adapter not initialized. Call initialize() before parsing messages.`);if(!n)throw new t(`line`,`Event has no valid source ID. Cannot construct thread ID.`);let r=e.source.userId??`unknown`,i=e.message.type===`text`?e.message.text??``:``,a=p(e.source.type,this.channelId,n),o=[];if(e.message.type!==`text`&&N.has(e.message.type)){let t=e.message.id;o.push({fetchData:async()=>I(await this.client.getMessageContent(t)),mimeType:R(e.message.type),name:`${e.message.type}-${t}`,type:e.message.type})}return new c({attachments:o,author:{fullName:``,isBot:!1,isMe:!1,userId:r,userName:r},formatted:this.converter.toAst(i),id:e.webhookEventId,metadata:{dateSent:new Date(e.timestamp),edited:!1},raw:e,text:i,threadId:a})}async postMessage(e,i){let{sourceId:a}=this.decodeThreadId(e),o=n(i),s=r(i);s.length>0&&this.logger.warn(`File attachments are not directly supported in LINE`,{count:s.length});let c=[];if(o){let e=this.converter.renderPostable({card:o});c.push({text:e,type:`text`})}else if(typeof i==`string`)c.push({text:i,type:`text`});else if(`text`in i&&typeof i.text==`string`)c.push({text:i.text,type:`text`});else if(`markdown`in i&&typeof i.markdown==`string`){let e=this.converter.renderPostable(i);c.push({text:e,type:`text`})}else if(`ast`in i&&i.ast){let e=this.converter.fromAst(i.ast);c.push({text:M(e),type:`text`})}if(c.length===0)throw new t(`line`,`No message content to send`);let l=c.slice(0,5),u=await this.client.pushMessage({messages:l,to:a});return this.buildRawMessage(u,``,e)}async stream(e,t,n){let{sourceId:r}=this.decodeThreadId(e),i,a=``,o=0;for await(let n of t){let t=z(n);if(t&&(a+=t,a.length>500&&o<5)){let t=await this.client.pushMessage({messages:[{text:a,type:`text`}],to:r});i=this.buildRawMessage(t,a,e),o+=1,a=``}}if(a&&o<5){let t=await this.client.pushMessage({messages:[{text:a,type:`text`}],to:r});i=this.buildRawMessage(t,a,e)}return i||(this.logger.debug(`Stream produced no content, skipping send`),this.buildEmptyRawMessage(e))}buildRawMessage(e,t,n){let r=e.sentMessages?.[0]?.id??``;return{id:r,raw:{deliveryContext:{isRedelivery:!1},message:{id:r,text:t,type:`text`},mode:`active`,replyToken:``,source:{type:`user`,userId:``},timestamp:Date.now(),type:`message`,webhookEventId:r},threadId:n}}buildEmptyRawMessage(e){return{id:``,raw:{deliveryContext:{isRedelivery:!1},message:{id:``,text:``,type:`text`},mode:`active`,replyToken:``,source:{type:`user`,userId:``},timestamp:Date.now(),type:`message`,webhookEventId:``},threadId:e}}editMessage(t,n,r){throw new e(`line`,`LINE does not support message editing`)}deleteMessage(t,n){throw new e(`line`,`LINE does not support message deletion`)}addReaction(t,n,r){throw new e(`line`,`LINE does not support reactions`)}removeReaction(t,n,r){throw new e(`line`,`LINE does not support reactions`)}fetchMessages(t,n){throw new e(`line`,`LINE does not provide message history`)}async fetchThread(e){let t=this.threadCache.get(e);if(t&&t.expires>Date.now())return t.info;let{sourceType:n,sourceId:r}=this.decodeThreadId(e),i=this.channelIdFromThreadId(e),a;return a=n===`user`?await this.fetchUserThread(e,i,r):n===`group`?await this.fetchGroupThread(e,i,r):{channelId:i,channelName:void 0,id:e,isDM:!1,metadata:{sourceType:`room`}},this.threadCache.set(e,{expires:Date.now()+300*1e3,info:a}),a}async fetchUserThread(e,t,n){try{return{channelId:t,channelName:void 0,id:e,isDM:!0,metadata:{displayName:(await this.client.getProfile(n)).displayName}}}catch{return{channelId:t,channelName:void 0,id:e,isDM:!0,metadata:{}}}}async fetchGroupThread(e,t,n){try{let r=await this.client.getGroupSummary(n);return{channelId:t,channelName:r.groupName,id:e,isDM:!1,metadata:{groupName:r.groupName}}}catch{return{channelId:t,channelName:void 0,id:e,isDM:!1,metadata:{}}}}async startTyping(e){if(!h(e))return;let t=this.lastTypingTime.get(e);if(t&&Date.now()-t<5e4)return;let{sourceId:n}=this.decodeThreadId(e);try{await this.client.acquireChatControl(n),this.lastTypingTime.set(e,Date.now())}catch(e){this.logger.debug(`Failed to acquire chat control`,{error:e})}}renderFormatted(e){return this.converter.fromAst(e)}getLineClient(){return this.client}getClient(){return this.client}};const H=e=>{let n=e?.channelAccessToken??process.env.LINE_CHANNEL_ACCESS_TOKEN,r=e?.channelSecret??process.env.LINE_CHANNEL_SECRET,i=[];if(n||i.push(`channelAccessToken (or set LINE_CHANNEL_ACCESS_TOKEN env var)`),r||i.push(`channelSecret (or set LINE_CHANNEL_SECRET env var)`),i.length>0)throw new t(`line`,`Missing required configuration:\n - ${i.join(`
|
|
4
|
+
- `)}\n\nGet your credentials at: https://developers.line.biz/console/`);return new V({channelAccessToken:n,channelSecret:r,logger:e?.logger,userName:e?.userName})};export{V as LineAdapter,B as LineFormatConverter,H as createLineAdapter,m as decodeThreadId,p as encodeThreadId,h as isDM,M as toPlainText};
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/lib/thread-id.ts","../src/lib/to-plain-text.ts","../src/adapter.ts","../src/index.ts"],"sourcesContent":["import type { LineThreadId } from \"../types.js\";\n\nconst LINE_THREAD_ID_PREFIX = \"line:\" as const;\n\n/**\n * Encode a LINE thread ID from its components.\n * Format: line:<channelId>:<sourceType>:<sourceId>\n */\nexport const encodeThreadId = (\n sourceType: \"user\" | \"group\" | \"room\",\n channelId: string,\n sourceId: string\n): string => `${LINE_THREAD_ID_PREFIX}${channelId}:${sourceType}:${sourceId}`;\n\n/**\n * Decode a LINE thread ID into its components.\n * @throws Error if the thread ID format is invalid\n */\nexport const decodeThreadId = (threadId: string): LineThreadId => {\n if (!threadId.startsWith(LINE_THREAD_ID_PREFIX)) {\n throw new Error(`Invalid LINE thread ID: ${threadId}`);\n }\n\n const rest = threadId.slice(LINE_THREAD_ID_PREFIX.length);\n const firstColon = rest.indexOf(\":\");\n\n if (firstColon === -1) {\n throw new Error(\n `Invalid LINE thread ID format: ${threadId}. Expected \"line:<channelId>:<sourceType>:<sourceId>\"`\n );\n }\n\n const channelId = rest.slice(0, firstColon);\n const remainder = rest.slice(firstColon + 1);\n const parts = remainder.split(\":\");\n\n if (parts.length < 2) {\n throw new Error(\n `Invalid LINE thread ID format: ${threadId}. Missing sourceType and sourceId.`\n );\n }\n\n const [sourceType] = parts;\n if (\n sourceType !== \"user\" &&\n sourceType !== \"group\" &&\n sourceType !== \"room\"\n ) {\n throw new Error(\n `Invalid source type in thread ID: ${sourceType}. Must be \"user\", \"group\", or \"room\"`\n );\n }\n\n const sourceId = parts.slice(1).join(\":\");\n\n return { channelId, sourceId, sourceType };\n};\n\n/**\n * Check if a thread ID represents a 1:1 DM.\n */\nexport const isDM = (threadId: string): boolean => {\n try {\n const { sourceType } = decodeThreadId(threadId);\n return sourceType === \"user\";\n } catch {\n return false;\n }\n};\n","/**\n * Compiled regex patterns for toPlainText.\n * Extracted to module-level to avoid recompilation on every call.\n */\nconst RE_HEADINGS = /^#{1,6}\\s+/gm;\nconst RE_BOLD_ITALIC = /\\*\\*\\*(.+?)\\*\\*\\*/g;\nconst RE_BOLD = /\\*\\*(.+?)\\*\\*/g;\nconst RE_ITALIC_STAR = /\\*(.+?)\\*/g;\nconst RE_ITALIC_UNDER = /__(.+?)__/g;\nconst RE_ITALIC_UNDER_SINGLE = /_(.+?)_/g;\nconst RE_STRIKETHROUGH = /~~(.+?)~~/g;\nconst RE_INLINE_CODE = /`(.+?)`/g;\nconst RE_CODE_BLOCK = /```[\\s\\S]*?```/g;\nconst RE_LINK = /\\[([^\\]]+)\\]\\([^)]+\\)/g;\nconst RE_IMAGE = /!\\[([^\\]]*)\\]\\([^)]+\\)/g;\nconst RE_BLOCKQUOTE = /^>\\s+/gm;\nconst RE_UNORDERED_LIST = /^[\\s]*[-*+]\\s+/gm;\nconst RE_ORDERED_LIST = /^[\\s]*\\d+\\.\\s+/gm;\nconst RE_HR = /^[-*_]{3,}\\s*$/gm;\nconst RE_EXTRA_BLANK_LINES = /\\n{3,}/g;\n\n/**\n * Convert Markdown text to plain text by stripping formatting.\n * LINE doesn't render Markdown, so this is used for outbound messages.\n */\nexport const toPlainText = (markdown: string): string =>\n markdown\n .replaceAll(RE_HEADINGS, \"\")\n .replaceAll(RE_BOLD_ITALIC, \"$1\")\n .replaceAll(RE_BOLD, \"$1\")\n .replaceAll(RE_ITALIC_STAR, \"$1\")\n .replaceAll(RE_ITALIC_UNDER, \"$1\")\n .replaceAll(RE_ITALIC_UNDER_SINGLE, \"$1\")\n .replaceAll(RE_STRIKETHROUGH, \"$1\")\n .replaceAll(RE_INLINE_CODE, \"$1\")\n .replaceAll(RE_CODE_BLOCK, (match) => match.replaceAll(\"```\", \"\").trim())\n .replaceAll(RE_LINK, \"$1\")\n .replaceAll(RE_IMAGE, \"$1\")\n .replaceAll(RE_BLOCKQUOTE, \"\")\n .replaceAll(RE_UNORDERED_LIST, \"\")\n .replaceAll(RE_ORDERED_LIST, \"\")\n .replaceAll(RE_HR, \"\")\n .replaceAll(RE_EXTRA_BLANK_LINES, \"\\n\\n\")\n .trim();\n","/* eslint-disable max-classes-per-file, class-methods-use-this */\nimport crypto from \"node:crypto\";\n\nimport {\n extractCard,\n extractFiles,\n PermissionError,\n ValidationError,\n} from \"@chat-adapter/shared\";\nimport { LineBotClient } from \"@line/bot-sdk\";\nimport type { messagingApi } from \"@line/bot-sdk\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Attachment,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n Logger,\n RawMessage,\n Root,\n StreamChunk,\n StreamOptions,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n BaseFormatConverter,\n ConsoleLogger,\n Message,\n deriveChannelId,\n parseMarkdown,\n stringifyMarkdown,\n} from \"chat\";\n\nimport { decodeThreadId, encodeThreadId, isDM } from \"./lib/thread-id.js\";\nimport { toPlainText } from \"./lib/to-plain-text.js\";\nimport type {\n LineAdapterConfig,\n LineMessageEvent,\n LineThreadId,\n LineWebhookPayload,\n} from \"./types.js\";\n\nconst VALID_ATTACHMENT_TYPES: ReadonlySet<string> = new Set([\n \"image\",\n \"video\",\n \"audio\",\n \"file\",\n]);\n\nconst verifySignature = (\n body: string,\n signature: string | null,\n channelSecret: string\n): boolean => {\n if (!signature) {\n return false;\n }\n\n const hash = crypto\n .createHmac(\"SHA256\", channelSecret)\n .update(body)\n .digest(\"base64\");\n\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(hash));\n};\n\nconst getSourceIdFromEvent = (event: LineMessageEvent): string | undefined => {\n const { source } = event;\n if (source.type === \"user\") {\n return source.userId;\n }\n if (source.type === \"group\") {\n return source.groupId;\n }\n return source.roomId;\n};\n\nconst readableToBuffer = async (\n readable: NodeJS.ReadableStream\n): Promise<Buffer> => {\n const chunks: Buffer[] = [];\n for await (const chunk of readable) {\n chunks.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk));\n }\n return chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);\n};\n\nconst isLineMessageEvent = (\n event: LineMessageEvent | Record<string, unknown>\n): event is LineMessageEvent =>\n event.type === \"message\" &&\n typeof event.source === \"object\" &&\n event.source !== null &&\n typeof (event.source as Record<string, unknown>).type === \"string\" &&\n typeof event.timestamp === \"number\" &&\n typeof event.replyToken === \"string\" &&\n typeof event.message === \"object\" &&\n event.message !== null &&\n typeof (event.message as Record<string, unknown>).type === \"string\" &&\n typeof (event.message as Record<string, unknown>).id === \"string\";\n\nconst getMimeType = (type: string): string => {\n if (type === \"image\") {\n return \"image/jpeg\";\n }\n if (type === \"video\") {\n return \"video/mp4\";\n }\n if (type === \"audio\") {\n return \"audio/mp4\";\n }\n return \"application/octet-stream\";\n};\n\nconst extractStreamText = (chunk: string | StreamChunk): string => {\n if (typeof chunk === \"string\") {\n return chunk;\n }\n if (chunk.type === \"markdown_text\") {\n return chunk.text;\n }\n return \"\";\n};\n\nexport class LineFormatConverter extends BaseFormatConverter {\n toAst(platformText: string): Root {\n return parseMarkdown(platformText);\n }\n\n fromAst(ast: Root): string {\n return stringifyMarkdown(ast);\n }\n\n override renderPostable(message: AdapterPostableMessage): string {\n const rendered = super.renderPostable(message);\n return toPlainText(rendered);\n }\n}\n\nexport class LineAdapter implements Adapter<LineThreadId, LineMessageEvent> {\n readonly name = \"line\";\n readonly userName: string;\n\n private chat: ChatInstance | null = null;\n private logger: Logger;\n private client: LineBotClient;\n private channelSecret: string;\n private channelId: string | null = null;\n private converter = new LineFormatConverter();\n private threadCache = new Map<\n string,\n { info: ThreadInfo; expires: number }\n >();\n private lastTypingTime = new Map<string, number>();\n\n constructor(config: LineAdapterConfig) {\n this.client = LineBotClient.fromChannelAccessToken({\n channelAccessToken: config.channelAccessToken,\n });\n this.channelSecret = config.channelSecret;\n this.userName = config.userName ?? \"line-bot\";\n this.logger = config.logger ?? new ConsoleLogger();\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n this.logger = chat.getLogger(\"line\");\n\n try {\n const botInfo = await this.client.getBotInfo();\n this.channelId = botInfo.userId;\n this.logger.info(\"LINE adapter initialized\", { botId: botInfo.userId });\n } catch (error) {\n this.logger.error(\"Failed to fetch bot info\", { error });\n this.channelId = \"unknown\";\n }\n }\n\n disconnect(): Promise<void> {\n this.chat = null;\n return Promise.resolve();\n }\n\n channelIdFromThreadId(threadId: string): string {\n return deriveChannelId(this, threadId);\n }\n\n encodeThreadId(data: LineThreadId): string {\n if (!this.channelId) {\n throw new ValidationError(\n \"line\",\n \"Channel ID not available. Ensure the adapter is initialized before encoding thread IDs.\"\n );\n }\n return encodeThreadId(data.sourceType, this.channelId, data.sourceId);\n }\n\n decodeThreadId(threadId: string): LineThreadId {\n return decodeThreadId(threadId);\n }\n\n async handleWebhook(\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n const signature = request.headers.get(\"x-line-signature\");\n const body = await request.text();\n\n if (!verifySignature(body, signature, this.channelSecret)) {\n return new Response(\"Invalid signature\", { status: 401 });\n }\n\n let payload: LineWebhookPayload;\n try {\n payload = JSON.parse(body);\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n if (!this.chat || !payload.events) {\n return new Response(\"OK\", { status: 200 });\n }\n\n const channelId = this.channelId ?? payload.destination;\n\n for (const event of payload.events) {\n if (!isLineMessageEvent(event)) {\n continue;\n }\n if (event.mode !== \"active\") {\n continue;\n }\n if (event.deliveryContext?.isRedelivery) {\n continue;\n }\n\n const sourceId = getSourceIdFromEvent(event);\n if (!sourceId) {\n continue;\n }\n\n const threadId = encodeThreadId(event.source.type, channelId, sourceId);\n\n const factory = (): Promise<Message<LineMessageEvent>> =>\n Promise.resolve(this.parseMessage(event));\n\n try {\n this.chat.processMessage(this, threadId, factory, options);\n } catch (error) {\n this.logger.error(\"processMessage failed\", {\n error,\n threadId,\n });\n }\n }\n\n return new Response(\"OK\", { status: 200 });\n }\n\n parseMessage(raw: LineMessageEvent): Message<LineMessageEvent> {\n const sourceId = getSourceIdFromEvent(raw);\n\n if (!this.channelId) {\n throw new ValidationError(\n \"line\",\n \"Adapter not initialized. Call initialize() before parsing messages.\"\n );\n }\n\n if (!sourceId) {\n throw new ValidationError(\n \"line\",\n \"Event has no valid source ID. Cannot construct thread ID.\"\n );\n }\n\n const userId = raw.source.userId ?? \"unknown\";\n const text = raw.message.type === \"text\" ? (raw.message.text ?? \"\") : \"\";\n const threadId = encodeThreadId(raw.source.type, this.channelId, sourceId);\n\n const attachments: Attachment[] = [];\n if (\n raw.message.type !== \"text\" &&\n VALID_ATTACHMENT_TYPES.has(raw.message.type)\n ) {\n const messageId = raw.message.id;\n attachments.push({\n fetchData: async () => {\n const stream = await this.client.getMessageContent(messageId);\n return readableToBuffer(stream);\n },\n mimeType: getMimeType(raw.message.type),\n name: `${raw.message.type}-${messageId}`,\n type: raw.message.type as Attachment[\"type\"],\n });\n }\n\n return new Message({\n attachments,\n author: {\n fullName: \"\",\n isBot: false,\n isMe: false,\n userId,\n userName: userId,\n },\n formatted: this.converter.toAst(text),\n id: raw.webhookEventId,\n metadata: {\n dateSent: new Date(raw.timestamp),\n edited: false,\n },\n raw,\n text,\n threadId,\n });\n }\n\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<LineMessageEvent>> {\n const { sourceId } = this.decodeThreadId(threadId);\n\n const card = extractCard(message);\n const files = extractFiles(message);\n\n if (files.length > 0) {\n this.logger.warn(\"File attachments are not directly supported in LINE\", {\n count: files.length,\n });\n }\n\n const lineMessages: messagingApi.Message[] = [];\n\n if (card) {\n const rendered = this.converter.renderPostable({ card });\n lineMessages.push({ text: rendered, type: \"text\" });\n } else if (typeof message === \"string\") {\n lineMessages.push({ text: message, type: \"text\" });\n } else if (\"text\" in message && typeof message.text === \"string\") {\n lineMessages.push({ text: message.text, type: \"text\" });\n } else if (\"markdown\" in message && typeof message.markdown === \"string\") {\n const rendered = this.converter.renderPostable(message);\n lineMessages.push({ text: rendered, type: \"text\" });\n } else if (\"ast\" in message && message.ast) {\n const rendered = this.converter.fromAst(message.ast);\n lineMessages.push({ text: toPlainText(rendered), type: \"text\" });\n }\n\n if (lineMessages.length === 0) {\n throw new ValidationError(\"line\", \"No message content to send\");\n }\n\n const messagesToSend = lineMessages.slice(0, 5);\n\n const result = await this.client.pushMessage({\n messages: messagesToSend,\n to: sourceId,\n });\n\n return this.buildRawMessage(result, \"\", threadId);\n }\n\n async stream(\n threadId: string,\n textStream: AsyncIterable<string | StreamChunk>,\n _options?: StreamOptions\n ): Promise<RawMessage<LineMessageEvent>> {\n const { sourceId } = this.decodeThreadId(threadId);\n let lastResult: RawMessage<LineMessageEvent> | undefined;\n let buffer = \"\";\n let sentCount = 0;\n\n for await (const chunk of textStream) {\n const text = extractStreamText(chunk);\n\n if (!text) {\n continue;\n }\n\n buffer += text;\n\n if (buffer.length > 500 && sentCount < 5) {\n const result = await this.client.pushMessage({\n messages: [{ text: buffer, type: \"text\" }],\n to: sourceId,\n });\n lastResult = this.buildRawMessage(result, buffer, threadId);\n sentCount += 1;\n buffer = \"\";\n }\n }\n\n if (buffer && sentCount < 5) {\n const result = await this.client.pushMessage({\n messages: [{ text: buffer, type: \"text\" }],\n to: sourceId,\n });\n lastResult = this.buildRawMessage(result, buffer, threadId);\n }\n\n if (!lastResult) {\n this.logger.debug(\"Stream produced no content, skipping send\");\n return this.buildEmptyRawMessage(threadId);\n }\n\n return lastResult;\n }\n\n private buildRawMessage(\n result: { sentMessages?: { id: string }[] },\n text: string,\n threadId: string\n ): RawMessage<LineMessageEvent> {\n const messageId = result.sentMessages?.[0]?.id ?? \"\";\n return {\n id: messageId,\n raw: {\n deliveryContext: { isRedelivery: false },\n message: {\n id: messageId,\n text,\n type: \"text\",\n },\n mode: \"active\",\n replyToken: \"\",\n source: { type: \"user\", userId: \"\" },\n timestamp: Date.now(),\n type: \"message\",\n webhookEventId: messageId,\n },\n threadId,\n };\n }\n\n private buildEmptyRawMessage(threadId: string): RawMessage<LineMessageEvent> {\n return {\n id: \"\",\n raw: {\n deliveryContext: { isRedelivery: false },\n message: { id: \"\", text: \"\", type: \"text\" },\n mode: \"active\",\n replyToken: \"\",\n source: { type: \"user\", userId: \"\" },\n timestamp: Date.now(),\n type: \"message\",\n webhookEventId: \"\",\n },\n threadId,\n };\n }\n\n editMessage(\n _threadId: string,\n _messageId: string,\n _message: AdapterPostableMessage\n ): Promise<RawMessage<LineMessageEvent>> {\n throw new PermissionError(\"line\", \"LINE does not support message editing\");\n }\n\n deleteMessage(_threadId: string, _messageId: string): Promise<void> {\n throw new PermissionError(\"line\", \"LINE does not support message deletion\");\n }\n\n addReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string\n ): Promise<void> {\n throw new PermissionError(\"line\", \"LINE does not support reactions\");\n }\n\n removeReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string\n ): Promise<void> {\n throw new PermissionError(\"line\", \"LINE does not support reactions\");\n }\n\n fetchMessages(\n _threadId: string,\n _options?: FetchOptions\n ): Promise<FetchResult<LineMessageEvent>> {\n throw new PermissionError(\"line\", \"LINE does not provide message history\");\n }\n\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const cached = this.threadCache.get(threadId);\n if (cached && cached.expires > Date.now()) {\n return cached.info;\n }\n\n const { sourceType, sourceId } = this.decodeThreadId(threadId);\n const channelId = this.channelIdFromThreadId(threadId);\n\n let result: ThreadInfo;\n\n if (sourceType === \"user\") {\n result = await this.fetchUserThread(threadId, channelId, sourceId);\n } else if (sourceType === \"group\") {\n result = await this.fetchGroupThread(threadId, channelId, sourceId);\n } else {\n result = {\n channelId,\n channelName: undefined,\n id: threadId,\n isDM: false,\n metadata: { sourceType: \"room\" },\n };\n }\n\n this.threadCache.set(threadId, {\n expires: Date.now() + 5 * 60 * 1000,\n info: result,\n });\n\n return result;\n }\n\n private async fetchUserThread(\n threadId: string,\n channelId: string,\n sourceId: string\n ): Promise<ThreadInfo> {\n try {\n const profile = await this.client.getProfile(sourceId);\n return {\n channelId,\n channelName: undefined,\n id: threadId,\n isDM: true,\n metadata: { displayName: profile.displayName },\n };\n } catch {\n return {\n channelId,\n channelName: undefined,\n id: threadId,\n isDM: true,\n metadata: {},\n };\n }\n }\n\n private async fetchGroupThread(\n threadId: string,\n channelId: string,\n sourceId: string\n ): Promise<ThreadInfo> {\n try {\n const summary = await this.client.getGroupSummary(sourceId);\n return {\n channelId,\n channelName: summary.groupName,\n id: threadId,\n isDM: false,\n metadata: { groupName: summary.groupName },\n };\n } catch {\n return {\n channelId,\n channelName: undefined,\n id: threadId,\n isDM: false,\n metadata: {},\n };\n }\n }\n\n async startTyping(threadId: string): Promise<void> {\n if (!isDM(threadId)) {\n return;\n }\n\n const last = this.lastTypingTime.get(threadId);\n if (last && Date.now() - last < 50_000) {\n return;\n }\n\n const { sourceId } = this.decodeThreadId(threadId);\n\n try {\n await this.client.acquireChatControl(sourceId);\n this.lastTypingTime.set(threadId, Date.now());\n } catch (error) {\n this.logger.debug(\"Failed to acquire chat control\", { error });\n }\n }\n\n renderFormatted(content: FormattedContent): string {\n return this.converter.fromAst(content);\n }\n\n /**\n * Returns the underlying LINE SDK client for operations not covered by the adapter.\n *\n * @example\n * ```ts\n * const client = adapter.getLineClient();\n * await client.getProfile(userId);\n * ```\n *\n * Note: Operations performed directly on this client bypass the adapter's\n * error handling and logging.\n */\n getLineClient(): LineBotClient {\n return this.client;\n }\n\n /**\n * @deprecated Use getLineClient() instead.\n */\n getClient(): LineBotClient {\n return this.client;\n }\n}\n","import { ValidationError } from \"@chat-adapter/shared\";\n\nimport { LineAdapter } from \"./adapter.js\";\nimport type { LineAdapterConfig } from \"./types.js\";\n\n/**\n * Create a LINE adapter with eager config validation.\n *\n * @example\n * ```ts\n * // With explicit config\n * const adapter = createLineAdapter({\n * channelAccessToken: \"eyJhbG...\",\n * channelSecret: \"abc123...\",\n * });\n *\n * // With environment variables\n * // LINE_CHANNEL_ACCESS_TOKEN and LINE_CHANNEL_SECRET must be set\n * const adapter = createLineAdapter();\n * ```\n */\nexport const createLineAdapter = (\n config?: Partial<LineAdapterConfig>\n): LineAdapter => {\n const channelAccessToken =\n config?.channelAccessToken ?? process.env.LINE_CHANNEL_ACCESS_TOKEN;\n const channelSecret =\n config?.channelSecret ?? process.env.LINE_CHANNEL_SECRET;\n\n const missing: string[] = [];\n\n if (!channelAccessToken) {\n missing.push(\n \"channelAccessToken (or set LINE_CHANNEL_ACCESS_TOKEN env var)\"\n );\n }\n\n if (!channelSecret) {\n missing.push(\"channelSecret (or set LINE_CHANNEL_SECRET env var)\");\n }\n\n if (missing.length > 0) {\n throw new ValidationError(\n \"line\",\n `Missing required configuration:\\n` +\n ` - ${missing.join(\"\\n - \")}\\n\\n` +\n `Get your credentials at: https://developers.line.biz/console/`\n );\n }\n\n return new LineAdapter({\n channelAccessToken: channelAccessToken as string,\n channelSecret: channelSecret as string,\n logger: config?.logger,\n userName: config?.userName,\n });\n};\n\nexport { LineAdapter, LineFormatConverter } from \"./adapter.js\";\nexport type {\n LineAdapterConfig,\n LineMessageEvent,\n LineRawMessage,\n LineThreadId,\n LineWebhookPayload,\n} from \"./types.js\";\nexport { decodeThreadId, encodeThreadId, isDM } from \"./lib/thread-id.js\";\nexport { toPlainText } from \"./lib/to-plain-text.js\";\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-adapter-line",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "LINE Messaging API adapter for Chat SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adapter",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"changeset:publish": "vp pack && changeset publish"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@changesets/cli": "^2.30.0",
|
|
53
52
|
"@chat-adapter/shared": "^4.23.0",
|
|
54
53
|
"@line/bot-sdk": "^11.0.0"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
56
|
+
"@changesets/cli": "^2.30.0",
|
|
57
57
|
"@types/node": "^25.5.2",
|
|
58
58
|
"chat": "^4.23.0",
|
|
59
59
|
"oxfmt": "^0.43.0",
|