-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
326 additions
and
235 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
"use strict"; | ||
import $log from './log.mjs'; | ||
|
||
/** | ||
* Settings for the command parser | ||
*/ | ||
const commandParserSettings = { | ||
prefix: ['!'], /**< Command prefix */ | ||
commands: new Map(), /**< Mapping of command name -> {n-ary function ( message, ...args ), [prefix, global]} */ | ||
enablePipes: true, /**< Enable unix-like pipes for commands */ | ||
}; | ||
|
||
export default { | ||
|
||
get commandParserSettings() { return commandParserSettings; }, | ||
set commandParserSettings(x) { commandParserSettings = x; }, | ||
|
||
_config_getter: null, | ||
|
||
get config() { return this._config_getter(); }, | ||
|
||
/** | ||
* Filter that checks whether the message is a command. | ||
* @param m Message object | ||
* @return @p m if it is a command, false otherwise. | ||
*/ | ||
isCommand(m) { | ||
const defaultPrefixes = commandParserSettings.prefix.some( p => m.message.startsWith( p ) ); | ||
if( defaultPrefixes ) return m; | ||
|
||
let customPrefix = false; | ||
for( const c of commandParserSettings.commands.values() ) { | ||
if( c.prefix === undefined ) continue; | ||
if( m.message.startsWith( c.prefix ) ) { | ||
customPrefix = true; | ||
break; | ||
} | ||
} | ||
|
||
return customPrefix ? m : false; | ||
}, | ||
|
||
/** | ||
* The command parser. The default message consumer. | ||
* Is this.configured in #commandParserSettings | ||
* @see commandParserSettings | ||
* @param m Message object | ||
*/ | ||
commandParser( m, ...extra ) { | ||
// removes prefix | ||
m.message = m.message.replace( commandParserSettings.prefix, "" ); | ||
const args = m.message.split(' '); | ||
|
||
const commandObject = commandParserSettings.commands.get(args[0]); | ||
if( commandObject ) { | ||
if( m.channel !== this.config.room && | ||
(commandObject.global !== undefined && !commandObject.global) ) return; | ||
args.shift(); | ||
return commandObject.command( m, ...args, ...extra ); | ||
} else { | ||
$log.info( `No command ${args[0]} found` ); | ||
} | ||
}, | ||
|
||
pipedCommandParser(m) { | ||
if( !commandParserSettings.enablePipes ) return this.commandParser(m); | ||
|
||
const commands = m.message.split('|').map( x => x.trim() ); | ||
|
||
const forgeMessage = string => { | ||
const c = JSON.parse( JSON.stringify( m ) ); | ||
c.message = string; | ||
return c; | ||
}; | ||
|
||
const forceShift = commands.shift(); | ||
const initial = x => this.commandParser( forgeMessage( forceShift ) ); | ||
|
||
const subsequents = commands.map( x => y => this.commandParser( forgeMessage( x ), y ) ); | ||
|
||
//probably lol | ||
const result = subsequents.reduce( | ||
( acc, x ) => { | ||
if( !acc ) { | ||
return x( ); | ||
} else if( typeof acc === "string" ) { | ||
return x( acc ); | ||
} else if( acc[0] ) { | ||
return x( acc[0] ); | ||
} | ||
return undefined; | ||
}, | ||
initial() | ||
); | ||
|
||
return result; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import TurndownService from 'turndown'; | ||
const turndownService = new TurndownService(); | ||
|
||
export const functional = { | ||
/** | ||
* The identity function | ||
* @see handleMessages() | ||
* @param a Anything | ||
* @return @p a | ||
*/ | ||
id(a) { return a; }, | ||
|
||
/** | ||
* Maybe monad composition for unary functions | ||
* If @p f or @p g or f(x) return a falsy value, it will propagate through | ||
* @param f Outer (later, 2nd) function | ||
* @param g Inner (earlier, 1st) function | ||
* @return New function that composes @p f and @p g, unless @p f, @p g, or the intermediary result is falsy (it returns undefined, i.e. Nothing) | ||
*/ | ||
maybeCompose( f, g ) { | ||
return x => { | ||
if( !f || !g ) return undefined; | ||
|
||
const y = g( x ); | ||
if( !y ) return undefined; | ||
else return f( x ); | ||
}; | ||
}, | ||
|
||
|
||
}; | ||
|
||
export const helpers = { | ||
/** | ||
* A transformer that converts received HTML into markdown. | ||
* @param m Message object | ||
* @return Message object with markdown body | ||
*/ | ||
reduceHtml(m) { | ||
const mp = m; | ||
|
||
const unescapeHtml = unsafe => { | ||
return unsafe | ||
.replace( /&/g, `&` ) | ||
.replace( /</g, `<` ) | ||
.replace( />/g, `>` ) | ||
.replace( /"/g, `"` ) | ||
.replace( /'/g, `'` ); | ||
}; | ||
|
||
// <p></p> | ||
mp.message = mp.message.replace( /<\/?p[\w =#"':\/\\.\-?]*>/gi, "" ); | ||
|
||
// <a>gets left</a> | ||
// Custom links are text in <a>, and then the link in <kbd> | ||
mp.message = mp.message.replace( /<\/?a[\w -=#"':\/\\.\-?]*>/gi, "" ); | ||
mp.message = mp.message.replace( "<kbd>", "" ); | ||
mp.message = mp.message.replace( "</kbd>", "" ); | ||
|
||
// <img> to :emotes: | ||
mp.message = mp.message.replace( /<img[\w -=#"':\/\\.\-?]*>/gi, m => { | ||
// (((, ))), :), :( are rendered differently | ||
// (They dont even show up in the emote list) | ||
// | ||
// It wouldn't be so bad if the two echos were 'echol' and 'echor', but | ||
// one echo is flipped with CSS. | ||
if( m.includes('alt="echo"') ) { | ||
return m.includes('scaleX(-1)') ? "(((" : ")))"; | ||
} | ||
return m.match( /alt="([\w:()]+)"/ )[1]; | ||
}); | ||
|
||
mp.message = turndownService.turndown( mp.message ); | ||
|
||
return mp; | ||
}, | ||
|
||
/** | ||
* Filter that checks message visibility. | ||
* Uses global config room and global. | ||
* @see config | ||
* @param m Message object | ||
* @return The message object if it is visible, undefined otherwise | ||
*/ | ||
roomCheck( m, config ) { | ||
return ( m.channel !== config.room && !config.global ) ? undefined : m; | ||
}, | ||
}; |
Oops, something went wrong.