UNPKG

intelligo

Version:

AI Chatbot Framework for Node.js

520 lines (456 loc) 11.6 kB
import { find } from 'lodash'; import { post } from 'request'; import * as extend from 'extend'; import * as WebSocket from 'ws'; import { EventEmitter } from 'eventemitter3'; import { setWsHeartbeat } from 'ws-heartbeat/client'; export class SlackBot extends EventEmitter { token: any; name: any; disconnect: any; wsUrl: any; self: any; team: any; channels: any; users: any; ims: any; groups: any; ws: any; /** * @param {object} params * @constructor */ constructor(params: any) { super(); this.token = params.token; this.name = params.name; this.disconnect = params.disconnect; console.assert(params.token, 'token must be defined'); if (!this.disconnect) { this.login(); } } /** * Starts a Real Time Messaging API session */ login() { this.api('rtm.start') .then((data: any) => { this.wsUrl = data.url; this.self = data.self; this.team = data.team; this.channels = data.channels; this.users = data.users; this.ims = data.ims; this.groups = data.groups; this.emit('start'); this.connect(); }) Promise.reject((data: any) => { this.emit('error', new Error(data.error ? data.error : data)); }) Promise.resolve(); } /** * Establish a WebSocket connection */ connect() { this.ws = new WebSocket(this.wsUrl); setWsHeartbeat(this.ws, '{ "kind": "ping" }'); this.ws.on('open', (data: any) => { this.emit('open', data); }); this.ws.on('close', (data: any) => { this.emit('close', data); }); this.ws.on('message', (data: any) => { try { this.emit('message', JSON.parse(data)); } catch (e) { console.log(e); } }); } /** * Get channels * @returns Promise */ getChannels() { if (this.channels) { return Promise.resolve({ channels: this.channels }); } return this.api('channels.list'); } /** * Get users */ getUsers() { if (this.users) { return Promise.resolve({ members: this.users }); } return this.api('users.list'); } /** * Get groups */ getGroups() { if (this.groups) { return Promise.resolve({ groups: this.groups }); } return this.api('groups.list'); } /** * Get user by name * @param name * @returns */ getUser(name: string) { return this.getUsers().then((data: any) => { var res = find(data.members, { name: name }); console.assert(res, 'user not found'); return res; }); } /** * Get channel by name * @param name * @returns {object} */ getChannel(name: string) { return this.getChannels().then((data: any) => { var res = find(data.channels, { name: name }); console.assert(res, 'channel not found'); return res; }); } /** * Get group by name * @param name * @returns {object} */ getGroup(name: string) { return this.getGroups().then((data: any) => { var res = find(data.groups, { name: name }); console.assert(res, 'group not found'); return res; }); } /** * Get user by id * @param id * @returns {object} */ getUserById(id: string) { return this.getUsers().then((data: any) => { var res = find(data.members, { id: id }); console.assert(res, 'user not found'); return res; }); } /** * Get channel by id * @param id * @returns {object} */ getChannelById(id: string) { return this.getChannels().then((data: any) => { var res = find(data.channels, { id: id }); console.assert(res, 'channel not found'); return res; }); } /** * Get group by id * @param id * @returns {object} */ getGroupById(id: string) { return this.getGroups().then((data: any) => { var res = find(data.groups, { id: id }); console.assert(res, 'group not found'); return res; }); } /** * Get channel ID * @param name * @returns */ getChannelId(name: string) { return this.getChannel(name).then((channel: any) => { return channel.id; }); } /** * Get group ID * @param name * @returns */ getGroupId(name: string): Promise<any> { return this.getGroup(name).then((group: any) => { return group.id; }); } /** * Get user ID * @param name * @returns */ getUserId(name: string) { return this.getUser(name).then((user: any) => { return user.id; }); } /** * Get user by email * @param email * @returns {object} */ getUserByEmail(email: string) { return this.getUsers().then((data: any) => { return find(data.members, { profile: { email: email } }); }); } /** * Get "direct message" channel ID * @param name * @returns {vow.Promise} */ getChatId(name: string): any { return this.getUser(name) .then((data: any) => { var chatId = find(this.ims, { user: data.id }); return (chatId && chatId.id) || this.openIm(data.id); }) .then((data: any) => { return typeof data === 'string' ? data : data.channel.id; }); } /** * Opens a "direct message" channel with another member of your Slack team * @param userId * @returns {vow.Promise} */ openIm(userId: string) { return this.api('im.open', { user: userId }); } /** * Get a list of all im channels * @returns {vow.Promise} */ getImChannels() { if (this.ims) { return Promise.resolve({ ims: this.ims }); } return this.api('im.list'); } /** * Posts an ephemeral message to a channel and user * @param id - channel ID * @param user - user ID * @param text * @param {object} params * @returns {vow.Promise} */ postEphemeral(id: string, user: string, text: string, params: any) { params = extend( { text: text, channel: id, user: user, username: this.name, }, params || {}, ); return this.api('chat.postEphemeral', params); } /** * Posts a message to a channel by ID * @param id - channel ID * @param text * @param {object} params * @returns {vow.Promise} */ postMessage(id: string, text: string, params: any) { params = extend( { text: text, channel: id, username: this.name, }, params || {}, ); return this.api('chat.postMessage', params); } /** * Updates a message by timestamp * @param id - channel ID * @param ts - timestamp * @param text * @param {object} params * @returns {vow.Promise} */ updateMessage(id: string, ts: string, text: string, params: any) { params = extend( { ts: ts, channel: id, username: this.name, text: text, }, params || {}, ); return this.api('chat.update', params); } /** * Posts a message to user by name * @param name * @param text * @param {object} params * @param {function} cb * @returns {vow.Promise} */ postMessageToUser(name: string, text: string, params: any, cb: any) { return this._post((params || {}).slackbot ? 'slackbot' : 'user', name, text, params, cb); } /** * Posts a message to channel by name * @param name * @param text * @param {object} params * @param {function} cb * @returns {vow.Promise} */ postMessageToChannel(name: string, text: string, params: any, cb: any) { return this._post('channel', name, text, params, cb); } /** * Posts a message to group by name * @param name * @param text * @param {object} params * @param {function} cb * @returns {vow.Promise} */ postMessageToGroup(name: string, text: string, params: any, cb: any) { return this._post('group', name, text, params, cb); } /** * Common method for posting messages * @param type * @param name * @param text * @param {object} params * @param {function} cb * @returns * @private */ _post(type: string, name: string, text: string, params: any, cb: any) { let method: any = { group: 'getGroupId', channel: 'getChannelId', user: 'getChatId', slackbot: 'getUserId', }[type]; if (typeof params === 'function') { cb = params; params = null; } return this[method](name) .then((itemId: any) => { return this.postMessage(itemId, text, params); }) .always((data: any) => { if (cb) { cb(data._value); } }); } /** * Posts a message to group | channel | user * @param name * @param text * @param {object} params * @param {function} cb * @returns {vow.Promise} */ postTo(name: string, text: string, params: any, cb: any) { return Promise.all([this.getChannels(), this.getUsers(), this.getGroups()]).then((data: any) => { name = this._cleanName(name); let all = [].concat(data[0].channels, data[1].members, data[2].groups); let result: any = find(all, { name: name }); console.assert(result, 'wrong name'); if (result['is_channel']) { return this.postMessageToChannel(name, text, params, cb); } else if (result['is_group']) { return this.postMessageToGroup(name, text, params, cb); } else { return this.postMessageToUser(name, text, params, cb); } }); } /** * Remove @ or # character from group | channel | user name * @param name * @returns */ _cleanName(name: string) { if (typeof name !== 'string') { return name; } var firstCharacter = name.charAt(0); if (firstCharacter === '#' || firstCharacter === '@') { name = name.slice(1); } return name; } /** * Preprocessing of params * @param params * @returns {object} * @private */ _preprocessParams(params: any) { params = extend(params || {}, { token: this.token }); Object.keys(params).forEach(function(name) { var param = params[name]; if (param && typeof param === 'object') { params[name] = JSON.stringify(param); } }); return params; } /** * Send request to API method * @param methodName * @param {object} params * @returns {vow.Promise} * @private */ api(methodName?: string, params?: any) { var data = { url: 'https://slack.com/api/' + methodName, form: this._preprocessParams(params), }; return new Promise((resolve: any, reject: any) => { post(data, (err, request, body) => { if (err) { reject(err); return false; } try { body = JSON.parse(body); // Response always contain a top-level boolean property ok, // indicating success or failure if (body.ok) { resolve(body); } else { reject(body); } } catch (e) { reject(e); } }); }); } }