import { Bot , Context , Session , Time , pick , segment , Dict , Schema } from 'koishi' ; export interface RefreshConfig { user ? : number ; guild ? : number ; channel ? : number ; } export const RefreshConfig = Schema . object ( { user : Schema . number ( ) . description ( '刷新用户数据的时间间隔。' ) . default ( Time . hour ) , guild : Schema . number ( ) . description ( '刷新群组数据的时间间隔。' ) . default ( Time . hour ) , channel : Schema . number ( ) . description ( '刷新频道数据的时间间隔。' ) . default ( Time . hour ) , } ) . description ( '刷新选项' ) ; const textSegmentTypes = [ 'text' , 'header' , 'section' ] ; const segmentTypes = { face : '表情' , record : '语音' , video : '短视频' , image : '图片' , music : '音乐' , quote : '引用' , forward : '合并转发' , dice : '掷骰子' , rps : '猜拳' , poke : '戳一戳' , json : 'JSON' , xml : 'XML' , card : '卡片消息' , } ; export interface Message { avatar ? : string ; content ? : string ; abstract ? : string ; username ? : string ; nickname ? : string ; platform ? : string ; messageId ? : string ; userId ? : string ; channelId ? : string ; guildId ? : string ; selfId ? : string ; channelName ? : string ; guildName ? : string ; timestamp ? : number ; quote ? : Message ; } async function getUserName ( bot , guildId , userId ) { try { const { username } = await bot . getGuildMember ( guildId , userId ) ; return username ; } catch { return userId ; } } async function getGroupName ( bot , guildId ) { try { const { guildName } = await bot . getGuild ( guildId ) ; return guildName ; } catch { return guildId ; } } async function getChannelName ( bot , channelId , guildId ) { try { const { channelName } = await bot . getChannel ( channelId , guildId ) ; return channelName ; } catch { return channelId ; } } export default function receiver ( ctx , config = { } ) { const { user : refreshUserName = Time . hour , guild : refreshGroupName = Time . hour , channel : refreshChannelName = Time . hour , } = config ; const channelMap = { } ; const groupMap = { } ; const userMap = { } ; ctx . on ( 'ready' , ( ) => { const timestamp = Date . now ( ) ; ctx . bots . forEach ( bot => userMap [ bot . sid ] = [ Promise . resolve ( bot . username ) , timestamp ] ) ; } ) ; async function prepareChannel ( session , params , timestamp ) { const { cid , guildId , channelName } = session ; if ( channelName ) { channelMap [ cid ] = [ Promise . resolve ( channelName ) , timestamp ] ; return ; } if ( ! guildId ) return ; if ( ! channelMap [ cid ] || timestamp - channelMap [ cid ] [ 1 ] >= refreshChannelName ) { channelMap [ cid ] = [ getChannelName ( session . bot , session . channelId , session . guildId ) , timestamp ] ; } params . channelName = await channelMap [ cid ] [ 0 ] ; } async function prepareGroup ( session , params , timestamp ) { const { cid , gid , guildId , guildName } = session ; if ( guildName ) { groupMap [ gid ] = [ Promise . resolve ( guildName ) , timestamp ] ; return ; } if ( ! guildId || cid === gid ) return ; if ( ! groupMap [ gid ] || timestamp - groupMap [ gid ] [ 1 ] >= refreshGroupName ) { groupMap [ gid ] = [ getGroupName ( session . bot , guildId ) , timestamp ] ; } params . guildName = await groupMap [ gid ] [ 0 ] ; } async function prepareAbstract ( session , params , timestamp ) { const codes = segment . parse ( params . content . split ( /\r?\n/ , 1 ) [ 0 ] ) ; params . abstract = '' ; for ( const code of codes ) { if ( textSegmentTypes . includes ( code . type ) ) { params . abstract += segment . unescape ( code . data . content ) ; } else if ( code . type === 'at' ) { if ( code . data . type === 'all' ) { params . abstract += '@全体成员' ; } else if ( code . data . type === 'here' ) { params . abstract += '@在线成员' ; } else if ( code . data . role ) { params . abstract += '@角色组' ; } else if ( session . subtype === 'group' ) { const id = `${ session . platform }:${ code . data . id }` ; if ( code . data . name ) { userMap [ id ] = [ Promise . resolve ( code . data . name ) , timestamp ] ; } else if ( ! userMap [ id ] || timestamp - userMap [ id ] [ 1 ] >= refreshUserName ) { userMap [ id ] = [ getUserName ( session . bot , session . guildId , code . data . id ) , timestamp ] ; } params . abstract += '@' + ( code . data . name = await userMap [ id ] [ 0 ] ) ; } else { params . abstract += '@' + session . bot . username ; } } else if ( code . type === 'share' || code . type === 'location' ) { params . abstract += `[分享:${ code . data . title }]` ; } else if ( code . type === 'contact' ) { params . abstract += `[推荐${ code . data . type === 'qq' ? '好友' : '群' }:${ code . data . id }]` ; } else { params . abstract += `[${ segmentTypes [ code . type ] || '未知' }]` ; } } params . content = codes . map ( ( { type , data } ) => segment ( type , data ) ) . join ( '' ) ; } async function prepareContent ( session , message , timestamp ) { message . content = await session . preprocess ( ) ; const tasks = [ prepareAbstract ( session , message , timestamp ) ] ; if ( message . quote = session . quote ) { tasks . push ( prepareAbstract ( session , message . quote , timestamp ) ) ; } await Promise . all ( tasks ) ; } async function handleMessage ( session ) { const params = pick ( session , [ 'content' , 'timestamp' , 'messageId' , 'platform' , 'selfId' , 'channelId' , 'channelName' , 'guildId' , 'guildName' , 'userId' , ] , true ) ; Object . assign ( params , pick ( session . author , [ 'username' , 'nickname' , 'avatar' ] , true ) ) ; if ( session . type === 'message' ) { userMap [ session . uid ] = [ Promise . resolve ( session . author . username ) , Date . now ( ) ] ; } const { cid , channelName } = session ; const timestamp = Date . now ( ) ; if ( channelName ) { session . channelName = channelName ; channelMap [ cid ] = [ Promise . resolve ( channelName ) , timestamp ] ; } await Promise . all ( [ prepareChannel , prepareGroup , prepareContent ] . map ( cb => cb ( session , params , timestamp ) ) ) ; ctx . emit ( 'chat/receive' , params , session ) ; } ctx . any ( ) . on ( 'message' , handleMessage ) ; ctx . any ( ) . on ( 'send' , handleMessage ) ; }