const Rx = require("rx"); const assert = require("assert"); const fs = require("fs"); require("./chat.rx"); Rx.config.longStackSupport = true; declare var Promise; import { Helper, BotManager, Context, Message, BotMatch, User, IMessagingAdaptor } from "./Core"; import { SlackConnector } from "./Slack/SlackConnector"; import { Matcher } from "./Matcher"; import { Language } from "./Language"; var defaultOptions = { spellcheck: true, requireMentionInPublic: true, timeout: 15000 }; export class Bot { name: string; options: any; _id: string; private _chatlock: any = {}; private _adaptor: IMessagingAdaptor; private _matches: any; private _internal: any; private _other$: any; private _middleware: any; private _slackToken: string; private _userId: string; public miss$: any; private plugins: any[]; //todo add options constructor(slackBotToken: string) { this._id = BotManager.add(this); this._slackToken = slackBotToken; this.plugins = []; //Object.assign(defaultOptions, options); this.options = defaultOptions; var me = this; this._matches = []; this._internal = new Rx.Subject(); this._other$ = new Rx.Subject(); this.miss$ = new Rx.Subject(); this.hear("help") .reply(m => { var help = "```"; help += " __ __ ______ __ ______ \n" help += " /\\ \\_\\ \\ /\\ ___\\ /\\ \\ /\\ == \\ \n" help += " \\ \\ __ \\\\ \\ __\\ \\ \\ \\____\\ \\ _-/ \n" help += " \\ \\_\\ \\_\\\\ \\_____\\\\ \\_____\\\\ \\_\\ \n" help += " \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/ \n\n" help += "Try these commands for more info:\n"; this.plugins.forEach(element => { help += " help " + element.name + " - " + element.description + "\n"; }); help += " ```"; return help; }) .start(); } register(plugin: any) { this.plugins.push({ name: plugin.pluginName, description: plugin.description }); plugin.apply(this); this.hear("help " + plugin.pluginName) .replyDirect(m => { return "" + fs.readFileSync("./ts/" + plugin.pluginName + "/help.md"); }) .start(); } getMatchList() { var matches = []; for (var p in this._matches) { if (this._matches.hasOwnProperty(p)) { matches.push(this._matches[p].match); } } return matches; } connect() { return new Promise((resolve, reject) => { this._adaptor = new SlackConnector(this._slackToken, sb => { this.name = sb.name; this._userId = sb.userId; resolve(); }); var o; if (this._middleware) { o = this._middleware(this._adaptor.messageBus.messages$); } else { o = this._adaptor.messageBus.messages$; } o .do(msg => { msg.direct = true; }) .where(msg => { if (!this.options.requireMentionInPublic) return true; return msg.direct; }) .merge(this._internal) .where( msg => !this._chatlock.hasOwnProperty(msg.channel + ":" + msg.user) ) .subscribe((msg: Message) => { var result = Matcher.findMatchFromListOfCommands( this.getMatchList(), msg.user, msg.text, this._id ); if (result.match) { var context = new Context( this._id, msg, result.regexResult.parameters ); this._matches[result.match].onNext(context); } else { var context = new Context(this._id, msg); this.miss$.onNext(context); } }); }); } inject(middleware) { this._middleware = middleware; } hear(s:string) { assert.notEqual(s, null); var botMatch = new BotMatch(s); this._matches[s] = botMatch; return botMatch.observable; } //todo put text parameter first! say(channel, text?, user?) { if (arguments.length == 1) { text = Helper.getRandom("" + channel); channel = this._adaptor.defaultChannel; } else { text = Helper.getRandom("" + text); } this._adaptor.messageBus.say(channel, text, user); } ask(channel, user, text, predicate) { if (!predicate) predicate = msg => true; this._chatlock[channel + ":" + user] = {}; return this.askInternal(channel, user, text, predicate).do( msg => delete this._chatlock[channel + ":" + user] ); } private askInternal(channel: string, user: User, text: string, predicate) { var timeout = Rx.Observable .return() .delay(this.options.timeout) .map(msg => "timed-out"); this.say(channel, text, user); var invalidResponse = this._adaptor.messageBus.messages$ .filter(x => { return x.user.id == user.id && x.channel == channel; }) .filter(x => !predicate(x.text)) .subscribe(msg => this.say(msg.channel, text, msg.user)); return this._adaptor.messageBus.messages$ .filter(x => { return x.user.id == user.id && x.channel == channel; }) .filter(x => predicate(x.text)) .merge(timeout) .take(1) .do(c => invalidResponse.dispose()); } reply(t, msg) { if (msg.channel.substring(0, 1) == "#") this.say(msg.channel, "" + t, msg.user); else this.say(msg.channel, t); } getUserById(id): User { return this._adaptor.userProvider.getUserById(id); } respondsTo() { var result = []; for (var p in this._matches) { result.push(p); } return result; } }