1+ import { messagingApi } from '@line/bot-sdk'
2+ import consola from 'consola'
3+ import { blobService , getMimeTypeFromExtension } from '../blob.ts'
4+ import type { GenericMessage , MessageResponse } from '../brain.ts'
5+
6+ // Convert LINE messages to generic format
7+ export async function handleLINEMessage (
8+ event : any ,
9+ lineConfig : { channelAccessToken : string }
10+ ) : Promise < GenericMessage > {
11+ if ( event . message . type === 'text' ) {
12+ return {
13+ type : 'text' ,
14+ userId : event . source . userId ,
15+ text : event . message . text
16+ }
17+ } else if ( [ 'image' , 'audio' , 'video' , 'file' ] . includes ( event . message . type ) ) {
18+ try {
19+ // Create blob client for LINE content
20+ const lineBlobClient = new messagingApi . MessagingApiBlobClient ( lineConfig )
21+
22+ // Get content from LINE
23+ const content = await lineBlobClient . getMessageContent ( event . message . id )
24+
25+ // Determine content type - LINE might not provide content type headers properly
26+ // for media messages, but we could try to infer from the file extension or message type
27+ const contentType = determineContentType ( event . message )
28+
29+ // Create a unique blob name
30+ const blobName = `${ event . source . userId } /${ Date . now ( ) } -${ event . message . id } .${ getFileExtension ( event . message ) } `
31+
32+ // Upload to Azure Blob Storage using our service
33+ const { blobKey, contentLength } = await blobService . uploadStream (
34+ 'ephemeral' ,
35+ blobName ,
36+ content ,
37+ contentType
38+ )
39+
40+ // After successful upload, return the message with the blob key
41+ return {
42+ type : event . message . type as 'audio' | 'video' | 'file' ,
43+ userId : event . source . userId ,
44+ blobKey,
45+ contentType,
46+ filename : event . message . fileName || `${ event . message . id } .${ getFileExtension ( event . message ) } ` ,
47+ size : contentLength
48+ }
49+ } catch ( error ) {
50+ consola . error ( 'Error uploading media to blob storage:' , error )
51+ throw new Error ( `Failed to upload media: ${ ( error as Error ) . message } ` )
52+ }
53+ } else {
54+ // For unsupported message types, return a text message with error
55+ throw new Error ( `Unsupported message type: ${ event . message . type } ` )
56+ }
57+ }
58+
59+ // Helper functions for file handling
60+ function determineContentType ( message : any ) : string {
61+ // Fallback to standard MIME types based on message type
62+ switch ( message . type ) {
63+ case 'image' :
64+ return 'image/jpeg'
65+ case 'audio' :
66+ return 'audio/mpeg'
67+ case 'video' :
68+ return 'video/mp4'
69+ case 'file' :
70+ // Try to determine from file extension
71+ const extension = getFileExtension ( message ) . toLowerCase ( )
72+ return getMimeTypeFromExtension ( extension ) || 'application/octet-stream'
73+ default :
74+ return 'application/octet-stream'
75+ }
76+ }
77+
78+ function getFileExtension ( message : any ) : string {
79+ if ( message . fileName ) {
80+ const parts = message . fileName . split ( '.' )
81+ if ( parts . length > 1 ) {
82+ return parts [ parts . length - 1 ]
83+ }
84+ }
85+
86+ // Default extensions based on type
87+ switch ( message . type ) {
88+ case 'image' : return 'jpg'
89+ case 'audio' : return 'mp3'
90+ case 'video' : return 'mp4'
91+ case 'file' : return 'bin'
92+ default : return 'bin'
93+ }
94+ }
95+
96+ // Removed: getMimeTypeFromExtension is now imported from blob.ts
97+
98+ // Send response back to LINE
99+ export async function sendLINEResponse (
100+ response : MessageResponse ,
101+ replyToken : string ,
102+ lineConfig : { channelAccessToken : string }
103+ ) : Promise < void > {
104+ const client = new messagingApi . MessagingApiClient ( lineConfig )
105+ if ( response . type === 'text' && response . content ) {
106+ await client . replyMessage ( {
107+ replyToken,
108+ messages : [
109+ {
110+ type : 'text' ,
111+ text : response . content ,
112+ } ,
113+ ] ,
114+ } )
115+ } else if ( response . type === 'media' && response . mediaUrl ) {
116+ // Handle media responses if needed
117+ await client . replyMessage ( {
118+ replyToken,
119+ messages : [
120+ {
121+ type : 'text' ,
122+ text : `Media URL: ${ response . mediaUrl } ` ,
123+ } ,
124+ ] ,
125+ } )
126+ } else if ( response . type === 'none' ) {
127+ // No response needed
128+ await client . replyMessage ( {
129+ replyToken,
130+ messages : [
131+ {
132+ type : 'text' ,
133+ text : 'Message received, but no response is needed.' ,
134+ } ,
135+ ] ,
136+ } )
137+ }
138+ }
0 commit comments