import { Body } from './body' ; import { ButtonAction , ActionHive , ActionFunc } from './action-hive' ; import { Choices , ChoicesRecord , generateChoicesButtons , combineHideAndChoices } from './choices' ; import { ChooseOptions } from './buttons/choose' ; import { ContextFunc , ContextPathFunc , ConstOrContextFunc , ConstOrContextPathFunc , RegExpLike } from './generic-types' ; import { ensureTriggerChild } from './path' ; import { Keyboard , ButtonTemplate , CallbackButtonTemplate , ButtonTemplateRow , InlineKeyboard } from './keyboard' ; import { MenuLike , Submenu } from './menu-like' ; import { PaginationOptions , createPaginationChoices , SetPageFunction } from './buttons/pagination' ; import { SelectOptions , generateSelectButtons } from './buttons/select' ; import { SingleButtonOptions } from './buttons/basic' ; import { SubmenuOptions , ChooseIntoSubmenuOptions } from './buttons/submenu' ; import { ToggleOptions , generateToggleButton } from './buttons/toggle' ; export interface InteractionOptions < Context > extends SingleButtonOptions < Context > { /**
     * Function which is called when the button is pressed
     */ readonly do : ActionFunc < Context > ; } export class MenuTemplate < Context > { private readonly _body : ContextPathFunc < Context , Body > ; private readonly _keyboard : Keyboard < Context > = new Keyboard ( ) ; private readonly _actions : ActionHive < Context > = new ActionHive ( ) ; private readonly _submenus : Set < Submenu < Context > > = new Set ( ) ; constructor ( body ) { this . _body = typeof body === 'function' ? body : ( ) => body ; } param context async renderBody ( context , path ) { return this . _body ( context , path ) ; } param context param path async renderKeyboard ( context , path ) { return this . _keyboard . render ( context , path ) ; } param path renderActionHandlers ( path ) { return this . _actions . list ( path ) ; } /**
     * Lists the submenus used in this menu template. Usage only recommended for advanced usage of this library.
     */ listSubmenus ( ) { return this . _submenus ; } param button param options manual ( button , options = { } ) { const { hide } = options ; if ( hide ) { this . _keyboard . add ( Boolean ( options . joinLastRow ) , async ( context , path ) => { if ( await hide ( context , path ) ) { return undefined ; } return typeof button === 'function' ? button ( context , path ) : button ; } ) ; } else { this . _keyboard . add ( Boolean ( options . joinLastRow ) , button ) ; } } param creator manualRow ( creator ) { this . _keyboard . addCreator ( creator ) ; } param trigger param action example manualAction ( trigger , action ) { this . _actions . add ( trigger , action , undefined ) ; } param text param url param options url ( text , url , options = { } ) { this . manual ( async ( context , path ) => ( { text : typeof text === 'function' ? await text ( context , path ) : text , url : typeof url === 'function' ? await url ( context , path ) : url , } ) , options ) ; } param text param query param options switchToChat ( text , query , options = { } ) { this . manual ( async ( context , path ) => ( { text : typeof text === 'function' ? await text ( context , path ) : text , switch_inline_query : typeof query === 'function' ? await query ( context , path ) : query , } ) , options ) ; } param text param query param options switchToCurrentChat ( text , query , options = { } ) { this . manual ( async ( context , path ) => ( { text : typeof text === 'function' ? await text ( context , path ) : text , switch_inline_query_current_chat : typeof query === 'function' ? await query ( context , path ) : query , } ) , options ) ; } param text param relativePath param options example example example navigate ( text , relativePath , options = { } ) { this . _keyboard . add ( Boolean ( options . joinLastRow ) , generateCallbackButtonTemplate ( text , relativePath , options . hide ) ) ; } param text param action param options example example interact ( text , action , options ) { if ( 'doFunc' in options ) { throw new TypeError ( 'doFunc was renamed to do' ) ; } if ( typeof options . do !== 'function' ) { throw new TypeError ( 'You have to specify `do` in order to have an interaction for this button. If you only want to navigate use `menuTemplate.navigate(…)` instead.' ) ; } this . _actions . add ( new RegExp ( action + '$' ) , options . do , options . hide ) ; this . _keyboard . add ( Boolean ( options . joinLastRow ) , generateCallbackButtonTemplate ( text , action , options . hide ) ) ; } param text param action param submenu param options example submenu ( text , action , submenu , options = { } ) { ensureTriggerChild ( action ) ; const actionRegex = new RegExp ( action + '/' ) ; if ( [ ... this . _submenus ] . map ( o => o . action . source ) . includes ( actionRegex . source ) ) { throw new Error ( `There is already a submenu with the action "${ action }". Change the action in order to access both menus.` ) ; } this . _submenus . add ( { action : actionRegex , hide : options . hide , menu : submenu , } ) ; this . _keyboard . add ( Boolean ( options . joinLastRow ) , generateCallbackButtonTemplate ( text , action + '/' , options . hide ) ) ; } param actionPrefix param choices param options choose ( actionPrefix , choices , options ) { if ( 'doFunc' in options ) { throw new TypeError ( 'doFunc was renamed to do' ) ; } if ( typeof options . do !== 'function' ) { throw new TypeError ( 'You have to specify `do` in order to have an interaction for the buttons.' ) ; } const trigger = new RegExp ( actionPrefix + ':(.+)$' ) ; this . _actions . add ( trigger , async ( context , path ) => options . do ( context , getKeyFromPath ( trigger , path ) ) , options . disableChoiceExistsCheck ? options . hide : combineHideAndChoices ( actionPrefix , choices , options . hide ) ) ; if ( options . setPage ) { const pageTrigger = new RegExp ( actionPrefix + 'P:(\\d+)$' ) ; this . _actions . add ( pageTrigger , setPageAction ( pageTrigger , options . setPage ) , options . hide ) ; } this . _keyboard . addCreator ( generateChoicesButtons ( actionPrefix , false , choices , options ) ) ; } param actionPrefix param choices param submenu param options example chooseIntoSubmenu ( actionPrefix , choices , submenu , options = { } ) { ensureTriggerChild ( actionPrefix ) ; const actionRegex = new RegExp ( actionPrefix + ':([^/]+)/' ) ; if ( [ ... this . _submenus ] . map ( o => o . action . source ) . includes ( actionRegex . source ) ) { throw new Error ( `There is already a submenu with the action "${ actionPrefix }". Change the action in order to access both menus.` ) ; } this . _submenus . add ( { action : actionRegex , hide : options . disableChoiceExistsCheck ? options . hide : combineHideAndChoices ( actionPrefix , choices , options . hide ) , menu : submenu , } ) ; if ( options . setPage ) { const pageTrigger = new RegExp ( actionPrefix + 'P:(\\d+)$' ) ; this . _actions . add ( pageTrigger , setPageAction ( pageTrigger , options . setPage ) , options . hide ) ; } this . _keyboard . addCreator ( generateChoicesButtons ( actionPrefix , true , choices , options ) ) ; } param actionPrefix param choices param options example example select ( actionPrefix , choices , options ) { if ( 'setFunc' in options || 'isSetFunc' in options ) { throw new TypeError ( 'setFunc and isSetFunc were renamed to set and isSet' ) ; } if ( typeof options . set !== 'function' || typeof options . isSet !== 'function' ) { throw new TypeError ( 'You have to specify `set` and `isSet` in order to work with select. If you just want to let the user choose between multiple options use `menuTemplate.choose(…)` instead.' ) ; } const trueTrigger = new RegExp ( actionPrefix + 'T:(.+)$' ) ; this . _actions . add ( trueTrigger , async ( context , path ) => { const key = getKeyFromPath ( trueTrigger , path ) ; return options . set ( context , key , true ) ; } , options . disableChoiceExistsCheck ? options . hide : combineHideAndChoices ( actionPrefix + 'T' , choices , options . hide ) ) ; const falseTrigger = new RegExp ( actionPrefix + 'F:(.+)$' ) ; this . _actions . add ( falseTrigger , async ( context , path ) => { const key = getKeyFromPath ( falseTrigger , path ) ; return options . set ( context , key , false ) ; } , options . disableChoiceExistsCheck ? options . hide : combineHideAndChoices ( actionPrefix + 'F' , choices , options . hide ) ) ; if ( options . setPage ) { const pageTrigger = new RegExp ( actionPrefix + 'P:(\\d+)$' ) ; this . _actions . add ( pageTrigger , setPageAction ( pageTrigger , options . setPage ) , options . hide ) ; } this . _keyboard . addCreator ( generateSelectButtons ( actionPrefix , choices , options ) ) ; } param actionPrefix param options pagination ( actionPrefix , options ) { if ( typeof options . getCurrentPage !== 'function' || typeof options . getTotalPages !== 'function' || typeof options . setPage !== 'function' ) { throw new TypeError ( 'You have to specify `getCurrentPage`, `getTotalPages` and `setPage`.' ) ; } const paginationChoices = async ( context ) => { const totalPages = await options . getTotalPages ( context ) ; const currentPage = await options . getCurrentPage ( context ) ; return createPaginationChoices ( totalPages , currentPage ) ; } ; const trigger = new RegExp ( actionPrefix + ':(\\d+)$' ) ; this . _actions . add ( trigger , setPageAction ( trigger , options . setPage ) , options . hide ) ; this . _keyboard . addCreator ( generateChoicesButtons ( actionPrefix , false , paginationChoices , { columns : 5 , hide : options . hide , } ) ) ; } param text param actionPrefix param options example example toggle ( text , actionPrefix , options ) { if ( 'setFunc' in options || 'isSetFunc' in options ) { throw new TypeError ( 'setFunc and isSetFunc were renamed to set and isSet' ) ; } if ( typeof options . set !== 'function' || typeof options . isSet !== 'function' ) { throw new TypeError ( 'You have to specify `set` and `isSet` in order to work with toggle. If you just want to implement something more generic use `interact`' ) ; } this . _actions . add ( new RegExp ( actionPrefix + ':true$' ) , async ( context , path ) => options . set ( context , true , path ) , options . hide ) ; this . _actions . add ( new RegExp ( actionPrefix + ':false$' ) , async ( context , path ) => options . set ( context , false , path ) , options . hide ) ; this . _keyboard . add ( Boolean ( options . joinLastRow ) , generateToggleButton ( text , actionPrefix , options ) ) ; } } function generateCallbackButtonTemplate < Context > ( text , relativePath , hide ) { return async ( context , path ) => { if ( await hide ?. ( context , path ) ) { return undefined ; } return { relativePath , text : typeof text === 'function' ? await text ( context , path ) : text , } ; } ; } function getKeyFromPath ( trigger , path ) { const match = new RegExp ( trigger . source , trigger . flags ) . exec ( path ) ; const key = match ?. [ 1 ] ; if ( ! key ) { throw new Error ( `Could not read key from path '${ path }' for trigger '${ trigger . source }'` ) ; } return key ; } function setPageAction < Context > ( pageTrigger , setPage ) { return async ( context , path ) => { const key = getKeyFromPath ( pageTrigger , path ) ; const page = Number ( key ) ; await setPage ( context , page ) ; return '.' ; } ; }