├── runqemu ├── Makefile ├── settings.js.example ├── README ├── package.json ├── config.sample ├── savestate ├── scripts └── lndir ├── limitcmd.pl ├── bless.js ├── evalcmd ├── manpages ├── evalbot.pl ├── index.js └── helppages /runqemu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exec qemu-system-x86_64 -monitor null -hda hda -net none -m 72 -nographic -loadvm 1 2>/dev/null << EOF 3 | $1 4 | $(date -u +%s) 5 | $2 6 | EOF 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | hda: shbot-kernel shbot.cpio.gz 4 | qemu-img create -f qcow2 hda.tmp 1M 5 | ./savestate hda.tmp save 6 | mv hda.tmp hda 7 | 8 | clean: 9 | rm -f fifo hda hda.tmp 10 | -------------------------------------------------------------------------------- /settings.js.example: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | server: 'irc.freenode.net', 4 | nick: 'botnick', 5 | irc: { 6 | userName: 'botuser', 7 | password: 'secret', 8 | debug: false, 9 | sasl: true, 10 | channels: ["#evalbot"] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Shbot is an IRC bot that runs shell code. It is a fork of evalbot 2 | (http://www.vidarholen.net/contents/evalbot/). 3 | 4 | To run shbot, you need an initramfs image and a kernel, which you can find here: 5 | https://github.com/geirha/shbot-initramfs/releases 6 | 7 | Save the files as `shbot.cpio.gz` and `shbot-kernel`, then run `make` 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shbot", 3 | "version": "0.0.1", 4 | "description": "Replacement for evalbot.pl", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Geir Hauge ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "blessed": "^0.1.81", 13 | "irc": "^0.5.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config.sample: -------------------------------------------------------------------------------- 1 | # The bot's nick 2 | $nick="shbot"; 3 | 4 | # The IRC server 5 | $server="irc.freenode.net"; 6 | 7 | # The owner's nick. Evalbot will flood this nick with private messages. 8 | $owner=""; 9 | 10 | # The passwords for !raw 11 | $password=""; 12 | 13 | # The password for nickserv (if registered) 14 | $nickserv=""; 15 | 16 | # The channels to join 17 | @channels=("#bash", "#evalbot", "#awk", "##sed", "##bash-fr"); 18 | 19 | # Just leave this 20 | true; 21 | -------------------------------------------------------------------------------- /savestate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | image="$1" 3 | 4 | mkfifo fifo # &> /dev/null 5 | [[ -r fifo && -w fifo && -p fifo ]] || { echo "fifo fail"; exit 1; } 6 | 7 | < fifo qemu-system-x86_64 -kernel shbot-kernel -initrd shbot.cpio.gz -hda "$image" -net none -m 72 -nographic -append irc | 8 | while read -r f 9 | do 10 | printf '%s\n' "$f" >&2 11 | if [[ $f = $'Ready\r' ]]; then 12 | printf '\x01c\nsavevm\nquit\n' 13 | fi 14 | done > fifo 15 | 16 | rm -f fifo 17 | -------------------------------------------------------------------------------- /scripts/lndir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | shopt -s extglob 3 | 4 | if (( $# != 2 )); then 5 | printf >&2 'Usage: lndir source dest\n' 6 | exit 1 7 | fi 8 | 9 | cd "$2" || exit 10 | lndir() { 11 | local file dir 12 | : "$PWD" 13 | for file in "$1"/*; do 14 | if [[ -d $file ]]; then 15 | dir=${file#"$1/"} 16 | [[ $file = /* ]] || file=../$file 17 | mkdir -p "$dir" && 18 | ( cd "$dir" && lndir "$file" ) 19 | else 20 | ln -s "$file" . 21 | fi 22 | done 23 | } 24 | 25 | lndir "$1" 26 | -------------------------------------------------------------------------------- /limitcmd.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | my @signals = qw< HUP INT QUIT TERM SEGV PIPE XCPU XFSZ ALRM >; 4 | my $signal = 'TERM'; 5 | 6 | 7 | 8 | sub signal_handler($) { 9 | my ($sig) = @_; 10 | kill $signal, $child; 11 | exit 255; 12 | } 13 | 14 | $child = fork; 15 | if($child == 0) { 16 | exec "./runqemu", $ARGV[0], $ARGV[1] or die "internal error :("; 17 | } 18 | 19 | foreach my $sig (@signals) { 20 | $SIG{$sig} = \&signal_handler; 21 | } 22 | 23 | alarm 6; 24 | while (($pid = wait) != -1 && $pid != $child) {} 25 | exit $? 26 | #exit ($pid == $child_pid ? 0 : 1); 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /bless.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | EventEmitter = require('events').EventEmitter, 3 | blessed = require('blessed'); 4 | 5 | function UserInterface() { 6 | if (!(this instanceof UserInterface)) 7 | return new UserInterface(); 8 | var self = this; 9 | this.screen = blessed.screen({smartCSR: true}); 10 | 11 | this.log = blessed.Log({ 12 | top: 0, 13 | left: 'center', 14 | width: '100%', 15 | height: '100%-3', 16 | border: { type: 'line' }, 17 | }); 18 | 19 | this.input = blessed.Textbox({ 20 | bottom: 0, 21 | left: 'center', 22 | width: '100%', 23 | height: 3, 24 | border: { type: 'line' }, 25 | }); 26 | 27 | this.input.on('focus', function() { 28 | self.input.readInput(onInput); 29 | }); 30 | this.input.key(['escape', 'C-c'], function(ch, key) { process.exit(0); }); 31 | 32 | function onInput(err, value) { 33 | if (err) { 34 | self.log.pushLine("-> Error: " + JSON.stringify(error)); 35 | } 36 | self.log.pushLine('-> ' + value); 37 | self.emit('line', value); 38 | self.input.clearValue(); 39 | self.screen.render(); 40 | self.input.readInput(onInput); 41 | } 42 | 43 | this.screen.append(this.log); 44 | this.screen.append(this.input); 45 | 46 | this.input.focus(); 47 | this.screen.render(); 48 | } 49 | util.inherits(UserInterface, EventEmitter); 50 | 51 | module.exports = UserInterface; 52 | -------------------------------------------------------------------------------- /evalcmd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | processHooks() { 4 | if [[ -f manpages && $2 = "man "[![:blank:]]* ]]; then 5 | local cmd args manpages 6 | read -r cmd args <<< "$2" 7 | source ./manpages && 8 | if [[ ${manpages[$args]} ]]; then 9 | printf '%s: %s\n' "$2" "${manpages[$args]}" 10 | exit 11 | fi 12 | elif [[ -f helppages && $2 = "help "[![:blank:]]* ]]; then 13 | local cmd args helppages 14 | read -r cmd args <<< "$2" 15 | source ./helppages && 16 | if [[ ${helppages[$args]} ]]; then 17 | printf '%s\n' "${helppages[$args]}" 18 | exit 19 | fi 20 | elif [[ $2 = trigger?(s) ]]; then 21 | printf 'Available triggers: ' 22 | 23 | local x limit=5 idx v 24 | for idx in "${indices[@]}"; do 25 | v=${versions[$idx]} x=0 26 | while [[ $v = *\# ]] && (( x++ < limit )); do 27 | v=${versions[$v]} 28 | done 29 | printf '%s %s; ' "$idx" "$v" 30 | done 31 | 32 | exit 33 | fi 34 | } 35 | 36 | if [[ $1 = -n ]]; then 37 | nopaste=1 38 | shift 39 | fi 40 | 41 | t=$1\# 42 | declare -A triggers='()' versions='()' 43 | indices=() 44 | while IFS=$'\t' read -r trigger shell version; do 45 | triggers[$trigger]=$shell 46 | versions[$trigger]=$version 47 | indices+=("$trigger") # to keep the order 48 | done < ./triggers 49 | [[ ${triggers[$t]} ]] || exit 50 | 51 | processHooks "$@" 52 | 53 | output=$(./limitcmd.pl "${triggers[$t]}" "$2" | expand | head -c 4242 | tr -d '\r' | 54 | awk '{ do { print substr($0, 1, 120) (length > 120 ? "\\" : ""); $0 = substr($0, 121); } while(length($0)>0); }'; exit "${PIPESTATUS[0]}"; ) 55 | # print a \ at the end of the line if it's too long 56 | result=$? 57 | lines=$(wc -l <<< "$output") 58 | 59 | shopt -s extglob 60 | 61 | if [[ $output == *( ) ]] 62 | then 63 | if (( result > 0 )) 64 | then 65 | echo "no output within the time limit" 66 | else 67 | echo "no output" 68 | fi 69 | exit 0 70 | fi 71 | 72 | if ((nopaste)); then 73 | printf '%s\n' "$output" 74 | else 75 | if (( lines <= 3 )); then 76 | printf '%s\n' "$output"; 77 | exit 0 78 | fi 79 | 80 | if url=$(curl -sfF 'f:1=<-' http://ix.io <<< "$output"); then 81 | etc="etc... ( $url )" 82 | else 83 | etc="and so forth... (but now the pastebin is sick of me)" 84 | fi 85 | 86 | printf '%s\n' "$output" | head -n 2 87 | printf '%s\n' "$etc" 88 | fi 89 | 90 | -------------------------------------------------------------------------------- /manpages: -------------------------------------------------------------------------------- 1 | declare -A manpages='([recode]="http://ix.io/aNB" [adu]="http://ix.io/aNG" [renice]="http://ix.io/aNz" [kill]="http://ix.io/aNy" [grep]="http://ix.io/aNx" [tty]="http://ix.io/aNH" [pwd]="http://ix.io/aNE" [printf]="http://ix.io/aND" [false]="http://ix.io/aNC" [basename]="http://ix.io/aNI" [hostname]="http://ix.io/aNK" [time]="http://ix.io/aNJ" [mawk]="http://ix.io/aNM" [cal]="http://ix.io/aNL" [bash32]="http://ix.io/aNN" [bash31]="http://ix.io/aNR" [tail]="http://ix.io/aNQ" [nl]="http://ix.io/aNP" [bash30]="http://ix.io/aSA" [bsh]="http://ix.io/aNX" [bzip2]="http://ix.io/aNW" [xargs]="http://ix.io/aNV" [pathchk]="http://ix.io/aNU" [ex]="http://ix.io/aNT" [chown]="http://ix.io/aNS" [umount]="http://ix.io/aO0" [expand]="http://ix.io/aNZ" [dd]="http://ix.io/aNY" [gawk]="http://ix.io/aO5" [bash]="http://ix.io/aOa" [bash4]="http://ix.io/aOa" [gawk4]="http://ix.io/aO5" [patch]="http://ix.io/aO4" [gencat]="http://ix.io/aO3" [df]="http://ix.io/aO2" [nice]="http://ix.io/aO6" [bash42]="http://ix.io/aO9" [bash2]="http://ix.io/aO8" [tabs]="http://ix.io/aO7" [bash3]="http://ix.io/aNN" [gawk3]="http://ix.io/aOb" [bash43]="http://ix.io/aOa" [bash40]="http://ix.io/aOd" [ksh]="http://ix.io/aOc" [bash41]="http://ix.io/aSa" [bash1]="http://ix.io/aOe" [rmdir]="http://ix.io/aOj" [nohup]="http://ix.io/aOi" [link]="http://ix.io/aOh" [join]="http://ix.io/aOg" [comm]="http://ix.io/aOf" [file]="http://ix.io/aOk" [logname]="http://ix.io/aOl" [sort]="http://ix.io/aOm" [bash+]="http://ix.io/aOo" [fold]="http://ix.io/aOn" [head]="http://ix.io/aOp" [tput]="http://ix.io/aOq" [uudecode]="http://ix.io/aOs" [tsort]="http://ix.io/aOr" [od]="http://ix.io/aOu" [du]="http://ix.io/aOt" [rm]="http://ix.io/aOB" [pax]="http://ix.io/aOA" [newgrp]="http://ix.io/aOz" [ed]="http://ix.io/aOy" [echo]="http://ix.io/aOx" [chmod]="http://ix.io/aOw" ["["]="http://ix.io/aOv" [gzip]="http://ix.io/aOF" [dirname]="http://ix.io/aOE" [date]="http://ix.io/aOD" [cat]="http://ix.io/aOC" [touch]="http://ix.io/aOI" [expr]="http://ix.io/aOH" [diff]="http://ix.io/aOG" [tee]="http://ix.io/aOK" [locale]="http://ix.io/aOJ" [mount]="http://ix.io/aOL" [split]="http://ix.io/aON" [sed]="http://ix.io/aOM" [sh]="http://ix.io/aOT" [sleep]="http://ix.io/aOP" [mv]="http://ix.io/aOO" [pr]="http://ix.io/aPw" [ps]="http://ix.io/aOR" [m4]="http://ix.io/aOQ" [tac]="http://ix.io/aOU" [dash]="http://ix.io/aOT" [uuencode]="http://ix.io/aOs" [uname]="http://ix.io/aOS" [bc]="http://ix.io/aOV" [mktemp]="http://ix.io/aOW" [awk]="http://ix.io/aO1" [who]="http://ix.io/aOY" [true]="http://ix.io/aOX" [paste]="http://ix.io/aP1" [mkdir]="http://ix.io/aP0" [cp]="http://ix.io/aOZ" [ln]="http://ix.io/aP3" [chgrp]="http://ix.io/aP2" [localedef]="http://ix.io/aP4" [test]="http://ix.io/aOv" [cmp]="http://ix.io/aP5" [mksh]="http://ix.io/aP7" [unlink]="http://ix.io/aP6" [strace]="http://ix.io/aP9" [tr]="http://ix.io/aP8" [id]="http://ix.io/aPa" [stty]="http://ix.io/aPc" [cut]="http://ix.io/aPb" [oawk]="http://ix.io/aPd" [cksum]="http://ix.io/aPe" [setsid]="http://ix.io/aPg" [compress]="http://ix.io/aPf" [env]="http://ix.io/aPi" [csplit]="http://ix.io/aPh" [wc]="http://ix.io/aPj" [mknod]="http://ix.io/aPn" [ls]="http://ix.io/aPm" [iconv]="http://ix.io/aPl" [fuser]="http://ix.io/aPk" [nawk]="http://ix.io/aO1" [unexpand]="http://ix.io/aPp" [find]="http://ix.io/aPo" [ldconfig]="http://ix.io/aPq" [strings]="http://ix.io/aPx" [getent]="http://ix.io/aPr" [logger]="http://ix.io/aPs" [mkfifo]="http://ix.io/aPu" [getconf]="http://ix.io/aPt" [jsh]="http://ix.io/aNX" [uniq]="http://ix.io/aPv" )' 2 | -------------------------------------------------------------------------------- /evalbot.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use utf8; 3 | use warnings; 4 | 5 | use Net::IRC; 6 | 7 | my $usage; 8 | $usage=" 9 | Run evalbot: ./evalbot.pl configfile 10 | 11 | Where configfile is an evalbot config file (see config.sample) 12 | "; 13 | 14 | if(!$ARGV[0]) { die "$usage"; } 15 | do "$ARGV[0]" or die "Error when doing config file"; 16 | 17 | 18 | if(!$nick) { die "nick unspecified"; } 19 | if(!$password) { die "password unspecified"; } 20 | if(!$server) { die "irc server unspecified"; } 21 | if(!$owner) { die "owner unspecified"; } 22 | if(scalar @channels == 0) { print "No channels specified. Continuing anyways\n"; } 23 | 24 | $SIG{'INT'} = 'my_sigint_catcher'; 25 | $SIG{'TERM'} = 'my_sigint_catcher'; 26 | $SIG{'QUIT'} = 'my_sigint_catcher'; 27 | $SIG{'ALRM'} = 'my_alarm'; 28 | sub my_sigint_catcher { 29 | exit(1); 30 | } 31 | 32 | 33 | # Set up the connection to the IRC server. 34 | my $irc = new Net::IRC; 35 | my $conn = $irc->newconn( Server => "$server", 36 | Nick => "$nick", 37 | Ircname => "shbot, owned by $owner, based on evalbot" ); 38 | 39 | 40 | my $joined=0; 41 | 42 | sub join_channels { 43 | foreach (@channels) { 44 | $conn->join( "$_" ); 45 | } 46 | $joined=1; 47 | } 48 | 49 | sub my_alarm { 50 | if(!$nickserv) { 51 | if ($joined==0) { 52 | join_channels(); 53 | } 54 | } 55 | $conn->privmsg("$nick", "ping"); 56 | alarm(60); 57 | } 58 | 59 | alarm(60); 60 | 61 | # Connect the handlers to the events. 62 | $conn->add_handler( 376, \&join_channel ); 63 | $conn->add_handler( 422, \&join_channel ); 64 | $conn->add_handler( 'public', \&message ); 65 | $conn->add_handler( 'msg', \&private ); 66 | $conn->add_handler( 'notice', \¬ice ); 67 | 68 | # Start the Net::IRC event loop. 69 | $irc->start; 70 | 71 | sub join_channel 72 | { 73 | my( $conn, $event ) = @_; 74 | print( "Currently online\n" ); 75 | } 76 | 77 | sub message 78 | { 79 | my( $conn, $event ) = @_; 80 | my( $msg ) = $event->args; 81 | 82 | if( $msg =~/^# botsnack$/ ) { 83 | $conn->privmsg($event->to, "Core dumped."); 84 | } elsif( $msg =~/^# botsmack$/ ) { 85 | $conn->privmsg($event->to, "Segmentation fault"); 86 | } elsif( $msg =~/^# who am i$/ ) { 87 | $conn->privmsg($event->to, $event->nick . ": " . $event->nick); 88 | } elsif( $msg =~/^([^#]*)# (.*)/ ) { 89 | open(FOO, "-|", "./evalcmd", "$1", "$2"); 90 | while() { 91 | $conn->privmsg($event->to, $event->nick . ": $_"); 92 | } 93 | close(FOO); 94 | } 95 | } 96 | 97 | sub private 98 | { 99 | my( $conn, $event ) = @_; 100 | my( $msg ) = $event->args; 101 | 102 | if($event->nick =~ /^$nick$/) { return; } #lol 103 | 104 | $conn->privmsg( "$owner", "< " . $event->nick . "> $msg" ); 105 | 106 | if($msg =~ /^!help$/) { 107 | $conn->privmsg( $event->nick, "Usage: # cmd" ); 108 | } elsif($msg =~ /^!raw $password (.*)/) { 109 | $conn->sl($1); 110 | } elsif( $msg =~/^([^#]*)# (.*)/ ) { 111 | open(FOO, "-|", "./evalcmd", "$1", "$2"); 112 | while() { 113 | $conn->privmsg($event->nick, "$_"); 114 | } 115 | close(FOO); 116 | } else { 117 | open(FOO, "-|", "./evalcmd", "4", "$msg"); 118 | while() { 119 | $conn->privmsg($event->nick, "$_"); 120 | } 121 | close(FOO); 122 | } 123 | } 124 | 125 | sub notice 126 | { 127 | my( $conn, $event ) = @_; 128 | my( $msg ) = $event->args; 129 | if ( $event->nick eq "NickServ" ) { 130 | if($joined==0) { 131 | if($nickserv) { 132 | $conn->privmsg("NickServ", "identify $nickserv"); 133 | } 134 | join_channels(); 135 | } 136 | } 137 | } 138 | 139 | 140 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var irc = require('irc'), 4 | settings = require('./settings.js'), 5 | child_process = require('child_process'), 6 | bless = require('./bless'), 7 | util = require('util'); 8 | 9 | var ui = bless(); 10 | console.log = function() { 11 | ui.log.pushLine(util.format.apply(util, arguments)); 12 | }; 13 | 14 | console.error = function() { 15 | ui.log.pushLine(util.format.apply(util, arguments)); 16 | }; 17 | 18 | 19 | function evalcmd(trigger, command, target, nick) { 20 | var prefix = ""; 21 | if (nick) 22 | prefix = nick+": "; 23 | 24 | // sanitize input 25 | command = command.replace(/\1/g,""); 26 | 27 | var proc = child_process.spawn('./evalcmd', [ trigger, command ]); 28 | var prev = ""; 29 | var lines = []; 30 | proc.stdout.on('data', function(chunk) { 31 | lines = (prev + chunk).split('\n'); 32 | prev = lines.pop(); 33 | lines.forEach(function(line) { 34 | client.say(target, prefix + line); 35 | }); 36 | }); 37 | proc.stdout.on('end', function() { 38 | if (prev) 39 | client.say(target, prefix + prev); 40 | }); 41 | proc.on('close', function(code) { 42 | console.log(trigger+"# "+command + " # exit code "+ code); 43 | }); 44 | } 45 | 46 | function timestamp() { 47 | var d = new Date(); 48 | return [ 49 | ('0'+d.getHours()).slice(-2), 50 | ('0'+d.getMinutes()).slice(-2), 51 | ('0'+d.getSeconds()).slice(-2) 52 | ].join(':'); 53 | } 54 | 55 | var client = new irc.Client(settings.server, settings.nick, settings.irc); 56 | 57 | client.addListener('error', function(err) { 58 | console.log("error: ", err); 59 | }); 60 | function pad(s, n) { 61 | if ( s.length < n ) 62 | s = (new Buffer(n).fill(' ').toString() + s).slice(-n) 63 | return s; 64 | } 65 | client.addListener('message', function(from, to, message) { 66 | console.log(timestamp() + " %s <%s> %s", pad(to, 10), pad(from, 10), message); 67 | }); 68 | client.addListener('kill', function(nick, reason, channels, message) { 69 | console.log("kill", nick, reason, channels, message); 70 | }); 71 | client.addListener('registered', function(message) { 72 | if (client.nick !== 'shbot') { 73 | client.say('nickserv', 'GHOST shbot') 74 | } 75 | 76 | console.log("registered", message); 77 | }); 78 | 79 | client.addListener('notice', function(nick, to, text, message) { 80 | if (nick === 'NickServ' && message.endsWith('has been ghosted')) { 81 | client.send('NICK', 'shbot'); 82 | } 83 | }); 84 | 85 | client.addListener("raw", function(message) { 86 | switch (message.command) { 87 | case "900": // You are now logged in as 88 | console.log(message.args[3]); 89 | break; 90 | } 91 | }); 92 | 93 | client.addListener("ping", function(server) { 94 | console.log(server,"ping"); 95 | }); 96 | 97 | client.addListener('message#', function(nick, channel, text, message) { 98 | if (nick === client.nick) 99 | return; 100 | 101 | var m; 102 | if (text === "# botsnack") 103 | client.say(channel, "Core dumped"); 104 | else if (text === "# botsmack") 105 | client.say(channel, "Segmentation fault"); 106 | else if (m = text.match(/^([^ #]*)# (.*)/)) 107 | evalcmd(m[1], m[2], channel, nick); 108 | }); 109 | 110 | 111 | client.addListener('pm', function(nick, text, message) { 112 | if (nick === client.nick) 113 | return; 114 | 115 | var m; 116 | if (text === "# botsnack") 117 | client.say(nick, "Core dumped"); 118 | else if (text === "# botsmack") 119 | client.say(nick, "Segmentation fault"); 120 | else if (m = text.match(/^([^ #]*)# (.*)/)) 121 | evalcmd(m[1], m[2], nick); 122 | else 123 | evalcmd("", text, nick); 124 | }); 125 | 126 | 127 | ui.on('line', function(value) { 128 | var match; 129 | if ( match = value.match(/^say\s+([^\s]+)\s(.*)/) ) { 130 | client.say(match[1], match[2]); 131 | } 132 | else if ( match = value.match(/^nick\s+(.+)/) ) { 133 | client.send('NICK', match[1]); 134 | } 135 | else if ( match = value.match(/^(join|part)\s+(.+)/) ) { 136 | if ( match[1] === 'join') 137 | client.join(match[2]); 138 | else if ( match[1] === 'part') 139 | client.part(match[2]); 140 | } 141 | else if ( match = value.match(/^(re|dis)?connect$/) ) { 142 | if (match[0] === 'disconnect') 143 | client.disconnect(); 144 | else 145 | client.connect(0); 146 | } 147 | else { 148 | console.log("Unknown command"); 149 | } 150 | }); 151 | -------------------------------------------------------------------------------- /helppages: -------------------------------------------------------------------------------- 1 | declare -A helppages='([kill]="kill: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec] -- http://ix.io/aQ3" [pushd]="pushd: pushd [-n] [+N | -N | dir] -- http://ix.io/aQa" [false]="false: false -- http://ix.io/aPW" [printf]="printf: printf [-v var] format [arguments] -- http://ix.io/aQ9" [pwd]="pwd: pwd [-LP] -- http://ix.io/aQb" [export]="export: export [-fn] [name[=value] ...] or export -p -- http://ix.io/aPV" [return]="return: return [n] -- http://ix.io/aQf" [time]="time: time [-p] pipeline -- http://ix.io/aSk" [exec]="exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...] -- http://ix.io/aPT" [popd]="popd: popd [-n] [+N | -N] -- http://ix.io/aQ8" [alias]="alias: alias [-p] [name[=value] ... ] -- http://ix.io/aPB" [break]="break: break [n] -- http://ix.io/aPE" ["for (("]="for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done -- http://ix.io/aSg" [let]="let: let arg [arg ...] -- http://ix.io/aQ4" [source]="source: source filename [arguments] -- http://ix.io/aQj" [until]="until: until COMMANDS; do COMMANDS; done -- http://ix.io/aSl" [declare]="declare: declare [-aAfFgilnrtux] [-p] [name[=value] ...] -- http://ix.io/aPN" [times]="times: times -- http://ix.io/aQm" [bind]="bind: bind [-lpsvPSVX] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command] -- http://ix.io/aPD" [hash]="hash: hash [-lr] [-p pathname] [-dt] [name ...] -- http://ix.io/aQ0" [eval]="eval: eval [arg ...] -- http://ix.io/aPS" [logout]="logout: logout [n] -- http://ix.io/aQ6" [suspend]="suspend: suspend [-f] -- http://ix.io/aQk" [while]="while: while COMMANDS; do COMMANDS; done -- http://ix.io/aSn" [function]="function: function name { COMMANDS ; } or name () { COMMANDS ; } -- http://ix.io/aSh" ["(( ... ))"]="(( ... )): (( expression )) -- http://ix.io/aSc" [shopt]="shopt: shopt [-pqsu] [-o] [optname ...] -- http://ix.io/aQi" [typeset]="typeset: typeset [-aAfFgilrtux] [-p] name[=value] ... -- http://ix.io/aQq" ["["]="[: [ arg... ] -- http://ix.io/aPA" [echo]="echo: echo [-neE] [arg ...] -- http://ix.io/aPQ" [wait]="wait: wait [-n] [id ...] -- http://ix.io/aQv" [compopt]="compopt: compopt [-o|+o option] [-DE] [name ...] -- http://ix.io/aPL" [disown]="disown: disown [-h] [-ar] [jobspec ...] -- http://ix.io/aPP" [unalias]="unalias: unalias [-a] name [name ...] -- http://ix.io/aQt" [jobs]="jobs: jobs [-lnprs] [jobspec ...] or jobs -x command [args] -- http://ix.io/aRC" [bg]="bg: bg [job_spec ...] -- http://ix.io/aPC" [compgen]="compgen: compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word] -- http://ix.io/aPJ" [help]="help: help [-dms] [pattern ...] -- http://ix.io/aQ1" [trap]="trap: trap [-lp] [[arg] signal_spec ...] -- http://ix.io/aQn" [case]="case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac -- http://ix.io/aSe" [dirs]="dirs: dirs [-clpv] [+N] [-N] -- http://ix.io/aPO" [%]="%: job_spec [&] -- http://ix.io/aSb" [enable]="enable: enable [-a] [-dnps] [-f filename] [name ...] -- http://ix.io/aPR" [history]="history: history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...] -- http://ix.io/aQ2" [true]="true: true -- http://ix.io/aQo" [umask]="umask: umask [-p] [-S] [mode] -- http://ix.io/aQs" [continue]="continue: continue [n] -- http://ix.io/aPM" [unset]="unset: unset [-f] [-v] [-n] [name ...] -- http://ix.io/aQu" [shift]="shift: shift [n] -- http://ix.io/aQh" [test]="test: test [expr] -- http://ix.io/aQl" [if]="if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi -- http://ix.io/aSi" [caller]="caller: caller [expr] -- http://ix.io/aPG" [complete]="complete: complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...] -- http://ix.io/aPK" [.]=".: . filename [arguments] -- http://ix.io/aPy" [getopts]="getopts: getopts optstring name [arg] -- http://ix.io/aPZ" [exit]="exit: exit [n] -- http://ix.io/aPU" [mapfile]="mapfile: mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] -- http://ix.io/aQ7" [set]="set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...] -- http://ix.io/aQg" ["{ ... }"]="{ ... }: { COMMANDS ; } -- http://ix.io/aSo" [fc]="fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command] -- http://ix.io/aPX" ["[[ ... ]]"]="Shell commands matching keyword \`[[ ... ]]'\'' -- http://ix.io/aSd" [command]="command: command [-pVv] command [arg ...] -- http://ix.io/aPI" [readarray]="readarray: readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] -- http://ix.io/aQd" [variables]="variables: variables - Names and meanings of some shell variables -- http://ix.io/aSm" [for]="for: for NAME [in WORDS ... ] ; do COMMANDS; done -- http://ix.io/aSf" [builtin]="builtin: builtin [shell-builtin [arg ...]] -- http://ix.io/aPF" [fg]="fg: fg [job_spec] -- http://ix.io/aPY" [select]="select: select NAME [in WORDS ... ;] do COMMANDS; done -- http://ix.io/aSj" [type]="type: type [-afptP] name [name ...] -- http://ix.io/aQp" [local]="local: local [option] name[=value] ... -- http://ix.io/aQ5" [coproc]="coproc: coproc [NAME] command [redirections] -- " [:]=":: : -- http://ix.io/aPz" [readonly]="readonly: readonly [-aAf] [name[=value] ...] or readonly -p -- http://ix.io/aQe" [read]="read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...] -- http://ix.io/aQc" [cd]="cd: cd [-L|[-P [-e]] [-@]] [dir] -- http://ix.io/aPH" [ulimit]="ulimit: ulimit [-SHabcdefilmnpqrstuvxT] [limit] -- http://ix.io/aQr" )' 2 | --------------------------------------------------------------------------------