import type { BridgeContext , Request , MatrixUser , WeakEvent , } from 'matrix-appservice-bridge' ; import { AGE_LIMIT_SECONDS , log , } from './config.js' ; import { SuperEvent } from './super-event.js' ; import type { AppserviceManager } from './appservice-manager.js' ; import type { DialogManager } from './dialog-manager.js' ; import type { MiddleManager } from './middle-manager.js' ; import type { WechatyManager } from './wechaty-manager.js' ; import type { UserManager } from './user-manager.js' ; import { Contact , FileBox , Message , MiniProgram , UrlLink } from 'wechaty' ; export class MatrixHandler { public appserviceManager ! : AppserviceManager ; public userManager ! : UserManager ; public wechatyManager ! : WechatyManager ; public middleManager ! : MiddleManager ; public dialogManager ! : DialogManager ; constructor ( ) { log . verbose ( 'MatrixHandler' , 'constructor()' ) ; } public setManager ( managers ) { this . appserviceManager = managers . appserviceManager ; this . dialogManager = managers . dialogManager ; this . userManager = managers . userManager ; this . middleManager = managers . middleManager ; this . wechatyManager = managers . wechatyManager ; } public async onEvent ( request , context ? ) { log . verbose ( 'MatrixHandler' , 'onEvent({type: "%s"}, {userId: "%s"})' , request . getData ( ) . type , context ?. senders . matrix . getId ( ) ) ; const superEvent = new SuperEvent ( request , context , this . appserviceManager , this . wechatyManager ) ; /**
         * Put all the logical to processEvent()
         * because we need to add a try {} wrapper to all the codes
         * to prevent un-catched rejection.
         */ try { await this . process ( superEvent ) ; } catch ( e ) { log . error ( 'MatrixHandler' , 'onEvent() rejection: %s' , e && e . message ) ; console . error ( e ) ; } } /**
     * Be aware that the queriedUser did not contains any data from userStore.
     */ public async onUserQuery ( queriedUser ) { log . verbose ( 'MatrixHandler' , 'onUserQuery("%s")' , JSON . stringify ( queriedUser ) ) ; return { } ; } /****************************************************************************
     * Private Methods                                                         *
     ****************************************************************************/ protected async process ( superEvent ) { log . verbose ( 'MatrixHandler' , 'process({type: %s})' , superEvent . type ( ) ) ; if ( superEvent . isRoomInvitation ( ) ) { if ( superEvent . targetIsBot ( ) ) { log . verbose ( 'MatrixHandler' , 'process() isRoomInvitation() appservice was invited' ) ; await this . processRoomInvitationForBot ( superEvent ) ; } else { log . verbose ( 'MatrixHandler' , 'process() isRoomInvitation() skipped for non-bot user: %s"' , superEvent . target ( ) ! . getId ( ) ) ; } return ; } switch ( superEvent . type ( ) ) { case 'm.room.message' : await this . processMatrixMessage ( superEvent ) ; break ; default : log . silly ( 'MatrixHandler' , 'process() default for type: ' + superEvent . type ( ) ) ; break ; } } protected async processRoomInvitationForBot ( superEvent ) { log . verbose ( 'MatrixHandler' , 'processRoomInvitationForBot()' ) ; await superEvent . acceptRoomInvitation ( ) ; const room = superEvent . room ( ) ; const sender = superEvent . sender ( ) ; const memberIdDict = await this . appserviceManager . bridge . getBot ( ) . getJoinedMembers ( room . getId ( ) ) ; const memberNum = Object . keys ( memberIdDict ) . length ; if ( memberNum === 2 ) { log . silly ( 'MatrixHandler' , 'process() room has 2 members, treat it as a direct room' ) ; await this . middleManager . setDirectMessageRoom ( room , { consumerId : sender . getId ( ) , matrixUserId : superEvent . target ( ) ! . getId ( ) , } ) ; const text = 'This room has been registered as your bridge management/status room.' ; await this . appserviceManager . sendMessage ( text , room ) ; } else { log . silly ( 'MatrixHandler' , 'process() room has %s(!=2) members, it is not a direct room' , memberNum ) ; } } protected async processMatrixMessage ( superEvent ) { log . verbose ( 'MatrixHandler' , 'processMatrixMessage(superEvent)' ) ; /**
         * Matrix age was converted from millisecond to seconds in SuperEvent
         */ if ( superEvent . age ( ) > AGE_LIMIT_SECONDS ) { log . verbose ( 'MatrixHandler' , 'processMatrixMessage() skipping event due to age %s > %s' , superEvent . age ( ) , AGE_LIMIT_SECONDS ) ; return ; } if ( superEvent . senderIsBot ( ) || superEvent . senderIsVirtual ( ) ) { log . verbose ( 'MatrixHandler' , 'processMatrixMessage() virtual or appservice sender "%s" found, skipped.' , superEvent . sender ( ) . getId ( ) ) ; return ; } const matrixUser = superEvent . sender ( ) ; const matrixRoom = superEvent . room ( ) ; try { const filehelper = await this . wechatyManager . filehelperOf ( matrixUser . getId ( ) ) ; if ( filehelper ) { await filehelper . say ( `Matrix user "${ matrixUser . getId ( ) }" in room "${ matrixRoom . getId ( ) }" said: "${ superEvent . event . content ! [ 'body' ] }"` ) ; } } catch ( e ) { log . warn ( 'MatrixHandler' , 'processMatrixMessage() filehelperOf() rejection: %s' , e . message ) ; } const room = superEvent . room ( ) ; if ( await this . middleManager . isDirectMessageRoom ( room ) ) { await this . processDirectMessage ( superEvent ) ; } else { await this . processGroupMessage ( superEvent ) ; } } protected async processDirectMessage ( superEvent ) { log . verbose ( 'MatrixHandler' , 'processDirectMessage()' ) ; const room = superEvent . room ( ) ; const { user , service } = await this . middleManager . directMessageUserPair ( room ) ; const wechatyEnabled = await this . userManager . isEnabled ( user ) ; if ( ! wechatyEnabled ) { await this . dialogManager . gotoEnableWechatyDialog ( superEvent ) ; return ; } /**
         * Enabled
         */ try { if ( this . appserviceManager . isBot ( service . getId ( ) ) ) { await this . dialogManager . gotoSetupDialog ( superEvent ) ; } else if ( this . appserviceManager . isVirtual ( service . getId ( ) ) ) { await this . bridgeToWechatIndividual ( superEvent ) ; } else { throw new Error ( 'unknown service id ' + service . getId ( ) ) ; } } catch ( e ) { const adminRoom = await this . middleManager . adminRoom ( user . getId ( ) ) ; const errorMsg = [ 'MatrixHandler - processDirectMessage() failed! ' , `from ${ service . getId ( ) } to ${ user . getId ( ) } with content "${ superEvent . text ( ) }": ` , 'Exception(might not logged in to WeChat?) ' , e && e . message , ] . join ( '' ) ; await this . appserviceManager . sendMessage ( errorMsg , adminRoom ) ; } } protected async processGroupMessage ( superEvent ) { log . verbose ( 'MatrixHandler' , 'processGroupMessage()' ) ; const matrixUser = superEvent . sender ( ) ; const isEnabled = this . userManager . isEnabled ( matrixUser ) ; if ( ! isEnabled ) { await this . dialogManager . gotoEnableWechatyDialog ( superEvent ) ; return ; } try { const wechatyRoom = await this . middleManager . wechatyRoom ( superEvent . room ( ) ) ; const message = await this . superEvent2Message ( superEvent ) ; await wechatyRoom . say ( message as any ) ; } catch ( e ) { log . error ( 'MatrixHandler' , 'processGroupMessage() rejection: %s' , e . message ) ; } } protected async bridgeToWechatIndividual ( superEvent ) { log . verbose ( 'MatrixHandler' , 'bridgeToWechatIndividual()' ) ; const room = superEvent . room ( ) ; const { service } = await this . middleManager . directMessageUserPair ( room ) ; const contact = await this . middleManager . wechatyUser ( service ) ; const message = await this . superEvent2Message ( superEvent ) ; await contact . say ( message as any ) ; } protected async superEvent2Message ( superEvent ) { const content = superEvent . event . content ! ; let mxcUrl = '' ; let httpsUrl = '' ; const body = superEvent . text ( ) ; let message = body ; switch ( content [ 'msgtype' ] ) { case 'm.text' : break ; case 'm.image' : case 'm.file' : mxcUrl = content [ 'url' ] as string ; httpsUrl = await this . appserviceManager . mxcUrlToHttp ( mxcUrl ) ; message = FileBox . fromUrl ( httpsUrl , body . indexOf ( '.' ) > - 1 || content [ 'msgtype' ] !== 'm.image' ? body : `${ mxcUrl . split ( '/' ) . pop ( ) }.gif` ) ; } return message ; } }