import markdown from "markdown-it" ; import stringArgv from "string-argv" ; import { CommandError } from "./errors" ; import { ApiError } from "./provisioning/api" ; import { MatrixMessageContent } from "./MatrixEvent" ; import { BridgePermissionLevel } from "./Config/Config" ; import { PermissionCheckFn } from "./Connections" ; const md = new markdown ( ) ; export const botCommandSymbol = Symbol ( "botCommandMetadata" ) ; export function botCommand ( prefix , helpOrOpts , requiredArgs = [ ] , optionalArgs = [ ] , includeUserId = false ) { if ( typeof helpOrOpts === "string" ) { return Reflect . metadata ( botCommandSymbol , { prefix , help : helpOrOpts , requiredArgs , optionalArgs , includeUserId , } ) ; } return Reflect . metadata ( botCommandSymbol , { prefix , ... helpOrOpts } ) ; } export interface BotCommandOptions { help : string ; requiredArgs ? : string [ ] ; optionalArgs ? : string [ ] ; includeUserId ? : boolean ; category ? : string ; permissionLevel ? : BridgePermissionLevel ; permissionService ? : string ; } type BotCommandResult = { status ? : boolean ; reaction ? : string ; } | undefined ; type BotCommandFunction = ( ... args : string [ ] ) => Promise < BotCommandResult > ; export type BotCommands = { [ prefix ] : { fn : BotCommandFunction ; } & BotCommandOptions ; } ; export type HelpFunction = ( cmdPrefix ? : string , categories ? : string [ ] ) => MatrixMessageContent ; export function compileBotCommands ( ... prototypes ) { const botCommands = { } ; const cmdStrs = { } ; prototypes . forEach ( prototype => { Object . getOwnPropertyNames ( prototype ) . forEach ( propetyKey => { const b = Reflect . getMetadata ( botCommandSymbol , prototype , propetyKey ) ; if ( b ) { const category = b . category || "default" ; const requiredArgs = b . requiredArgs ?. join ( " " ) || "" ; const optionalArgs = b . optionalArgs ?. map ( ( arg ) => `[${ arg }]` ) . join ( " " ) || "" ; cmdStrs [ category ] = cmdStrs [ category ] || [ ] ; cmdStrs [ category ] . push ( ` - \`££PREFIX££${ b . prefix }\` ${ requiredArgs } ${ optionalArgs } - ${ b . help }` ) ; botCommands [ b . prefix as string ] = { fn : prototype [ propetyKey ] , help : b . help , requiredArgs : b . requiredArgs , optionalArgs : b . optionalArgs , includeUserId : b . includeUserId , category : b . category , } ; } } ) ; } ) ; return { helpMessage : ( cmdPrefix ? , onlyCategories ? ) => { let content = "" ; for ( const [ categoryName , commands ] of Object . entries ( cmdStrs ) ) { if ( categoryName !== "default" && onlyCategories && ! onlyCategories . includes ( categoryName ) ) { continue ; } if ( categoryName !== "default" ) { content += `### ${ categoryName [ 0 ] . toUpperCase ( ) }${ categoryName . substring ( 1 ) . toLowerCase ( ) }\n` ; } content += commands . join ( '\n' ) + "\n" ; } return { msgtype : "m.notice" , body : content . replace ( /££PREFIX££/g , cmdPrefix || "" ) , formatted_body : md . render ( content ) . replace ( /££PREFIX££/g , cmdPrefix || "" ) , format : "org.matrix.custom.html" } ; } , botCommands , } ; } export async function handleCommand ( userId , command , botCommands , obj , permissionCheckFn , defaultPermissionService ? , prefix ? ) { if ( prefix ) { if ( ! command . startsWith ( prefix ) ) { return { handled : false } ; } command = command . substring ( prefix . length ) ; } const parts = stringArgv ( command ) ; for ( let i = parts . length ; i > 0 ; i -- ) { const prefix = parts . slice ( 0 , i ) . join ( " " ) . toLowerCase ( ) ; const command = botCommands [ prefix ] ; if ( command ) { const permissionService = command . permissionService || defaultPermissionService ; if ( permissionService && ! permissionCheckFn ( permissionService , command . permissionLevel || BridgePermissionLevel . commands ) ) { return { handled : true , error : "You do not have permission to use this command" } ; } if ( command . requiredArgs && command . requiredArgs . length > parts . length - i ) { return { handled : true , error : "Missing args" } ; } const args = parts . slice ( i ) ; if ( command . includeUserId ) { args . splice ( 0 , 0 , userId ) ; } try { const result = await botCommands [ prefix ] . fn . apply ( obj , args ) ; return { handled : true , result } ; } catch ( ex ) { const commandError = ex as CommandError ; if ( ex instanceof ApiError ) { return { handled : true , error : ex . error , humanError : ex . error } ; } return { handled : true , error : commandError . message , humanError : commandError . humanError } ; } } } return { handled : false } ; }