Skip to content

Commit

Permalink
v0.2.0 - Production ready
Browse files Browse the repository at this point in the history
  • Loading branch information
diingus authored Jul 7, 2020
2 parents 3a0be39 + 50a1b28 commit 6eecfa6
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 235 deletions.
98 changes: 98 additions & 0 deletions commandParser.mjs
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;
},
};
88 changes: 88 additions & 0 deletions helpers.mjs
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( /&amp;/g, `&` )
.replace( /&lt;/g, `<` )
.replace( /&gt;/g, `>` )
.replace( /&quot;/g, `"` )
.replace( /&#39;/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;
},
};
Loading

0 comments on commit 6eecfa6

Please sign in to comment.