forked from NarubyRiverlione/slack-tipbot-Dash
-
Notifications
You must be signed in to change notification settings - Fork 11
/
bot.js
370 lines (328 loc) · 14.8 KB
/
bot.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
'use strict';
let _ = require('lodash');
let debug = require('debug');
let Botkit = require('botkit');
let assert = require('assert');
let parseArgs = require('minimist');
let mongoose = require('mongoose');
let autoIncrement = require('mongoose-auto-increment');
let async = require('async');
let argv = parseArgs(process.argv.slice(2));
const SLACK_TOKEN = argv['slack-token'] || process.env.TIPBOT_SLACK_TOKEN;
const RPC_USER = argv['rpc-user'] || process.env.TIPBOT_RPC_USER;
const RPC_PASSWORD = argv['rpc-password'] || process.env.TIPBOT_RPC_PASSWORD;
const RPC_PORT = argv['rpc-port'] || process.env.TIPBOT_RPC_PORT || 51470;
const WALLET_PASSW = argv['wallet-password'] || process.env.TIPBOT_WALLET_PASSWORD;
const debugMode = process.env.NODE_ENV === 'development' ? true : false;
const TIPBOT_OPTIONS = {
WALLET_PASSW: WALLET_PASSW,
ALL_BALANCES: true,
OTHER_BALANCES: true,
ENABLE_SUN_FEATURE: false,
ENABLE_QUIZ_FEATURE: false,
WARN_MODS_NEW_USER: !debugMode,
WARN_MODS_USER_LEFT: !debugMode,
SUN_USERNAME: 'pivxsun',
SUN_TIMER: debugMode ? 30 : 30 // debug = check sun every minute, production check every 30 minutes
};
let OPTIONS = {
PRICE_CHANNEL_NAME: debugMode ? 'test_channel' : 'price_speculation',
WARN_MODS_USER_LEFT_CHANNELNAME: debugMode ? 'test_channel' : 'moderators',
WARN_NEW_USER_CHANNELNAME: debugMode ? 'test_channel' : 'pivx_chat',
MAIN_CHANNEL_NAME: debugMode ? 'test_channel' : 'pivx_chat',
SHOW_RANDOM_HELP_TIMER: 720, // show a random help command every X minutes (6/12 hours = 360/720 minutes)
DB: 'mongodb://localhost/tipdb-dev' //tipbotdb
};
let initializing = 0;
let tipbot = null;
// decrease ticker until 0 => check sun balance > thershold
let sunTicker = 0;
// decrease ticker until 0 => show random help command text
let helpTicker = OPTIONS.SHOW_RANDOM_HELP_TIMER === undefined ? 0 : OPTIONS.SHOW_RANDOM_HELP_TIMER * 60;
assert(SLACK_TOKEN, '--slack-token or TIPBOT_SLACK_TOKEN is required');
assert(RPC_USER, '--rpc-user or TIPBOT_RPC_USER is required');
assert(RPC_PASSWORD, '--rpc-password or TIPBOT_RPC_PASSWORD is required');
/*
1) setup slack controller
2) connect to mongoDb
3) connect to slack
4) 'hello' = connected => setup tipbot
*/
debug('tipbot:bot')('starting up')
// setup Slack Controller
let controller = Botkit.slackbot({
logLevel: 5,
debug: true
//include 'log: false' to disable logging
//or a 'logLevel' integer from 0 to 7 to adjust logging verbosity
});
// open mongoDB connection if needed for a feature
const needMongoDb = TIPBOT_OPTIONS.ENABLE_SUN_FEATURE || TIPBOT_OPTIONS.ENABLE_QUIZ_FEATURE;
if (needMongoDb) {
mongoose.connect(OPTIONS.DB, { config: { autoIndex: debugMode } }); // no autoIndex in production for preformance impact
let db = mongoose.connection;
db.on('error', function (err) {
debug('tipbot:db')('******** ERROR: unable to connect to database at ' + OPTIONS.DB + ': ' + err);
});
// database connection open = conncect to slack
db.once('open', function () {
autoIncrement.initialize(db);
require('./model/tipper'); // load mongoose Tipper model
require('./model/quiz');// load mongoose Quiz model
debug('tipbot:db')('********* Database connected ********');
// make connnection to Slack
connect(controller);
});
} else {
debug('tipbot:init')('No features enabled that need mongoDb.');
// no mongoDB needed, connect now to slack
connect(controller);
}
// connection to slack (function so it can be used to reconnect)
function connect(controller) {
// spawns the slackbot
controller.spawn({
token: SLACK_TOKEN,
retry: 10
}).startRTM(function (err, bot, payload) {
if (err) {
throw new Error(err);
}
// get info where bot is active
let channels = [],
groups = [];
_.each(payload.channels, function (channel) {
if (channel.is_member) {
channels.push('#' + channel.name);
}
});
_.each(payload.groups, function (group) {
if (group.is_open && !group.is_archived) {
groups.push(group.name);
}
});
debug('tipbot:bot')('******** Connected to Slack ********');
debug('tipbot:bot')('You are <@%s:%s> of %s', payload.self.id, payload.self.name, payload.team.name);
debug('tipbot:bot')('You are in (channels): %s', channels.join(', '));
debug('tipbot:bot')('As well as (groups): %s', groups.join(', '));
});
}
// connection to Slack has ended
controller.on('rtm_close', function () {
debug('tipbot:bot')('!!!!!! BOTKIT CLOSED DOWN !!!!!!!!');
//don't restart connection on error here because these an auto reconnect
});
// botkit had an oopsie
controller.on('error', function (bot, msg) {
debug('tipbot:bot')('+++++++++++++++ Slack Error!! +++++++++++++++');
debug('tipbot:bot')('ERROR code:' + msg.error.code + ' = ' + msg.error.msg);
// don't restart connection on error here because it will be restarted on the rtm_close event
});
// when bot is connected, get all needed channels
controller.on('hello', function (bot) {
// prevent multiple connections
// debug('tipbot:init')('Start Hello, Init count is now ' + initializing);
if (initializing > 0) {
debug('tipbot:bot')('Already initializing... (count ' + initializing + ')');
return;
}
initializing++;
// setup tipbot
if (tipbot === null) {
debug('tipbot:bot')('******** Setup TipBot ********');
// load TipBot after mongoose model is loaded
var TipBot = require('./lib/tipbot');
tipbot = new TipBot(bot, RPC_USER, RPC_PASSWORD, RPC_PORT, TIPBOT_OPTIONS);
}
let setChannelTasks = [];
// find channelID of PRICE_CHANNEL_NAME to broadcast price messages
setChannelTasks.push(
function (asyncCB) {
setChannel(OPTIONS.PRICE_CHANNEL_NAME, 'PRICETICKER_CHANNEL', 'No price channel to broadcast', asyncCB);
});
// if (OPTIONS.PRICE_CHANNEL_NAME !== undefined) {
// tipbot.getChannel(OPTIONS.PRICE_CHANNEL_NAME, function (err, priceChannel) {
// if (err) {
// debug('tipbot:bot')('Init: No price channel to broadcast.');
// } else {
// debug('tipbot:bot')('Init: Price channel ' + OPTIONS.PRICE_CHANNEL_NAME + ' = ' + priceChannel.id);
// // tell all prices on the price list
// tipbot.OPTIONS.PRICETICKER_CHANNEL = priceChannel;
// }
// });
// }
// find channelID of WARN_NEW_USER_CHANNEL to post new user warning messages
setChannelTasks.push(
function (asyncCB) {
setChannel(OPTIONS.WARN_NEW_USER_CHANNELNAME, 'WARN_NEW_USER_CHANNEL', ' warn new user channel', asyncCB);
});
// if (OPTIONS.WARN_NEW_USER_CHANNELNAME !== undefined) {
// tipbot.getChannel(OPTIONS.WARN_NEW_USER_CHANNELNAME, function (err, warnNewUserChannel) {
// if (err) {
// debug('tipbot:bot')('ERROR: ' + OPTIONS.WARN_NEW_USER_CHANNELNAME + ' channel not found!');
// } else {
// debug('tipbot:bot')('Init: channel ' + OPTIONS.WARN_NEW_USER_CHANNELNAME + ' = ' + warnNewUserChannel.id);
// // set new user warning channel for tipbot
// tipbot.OPTIONS.WARN_NEW_USER_CHANNEL = warnNewUserChannel;
// }
// });
// }
// find channelID of WARN_NEW_USER_CHANNEL to post new user warning messages
setChannelTasks.push(
function (asyncCB) {
setChannel(OPTIONS.WARN_MODS_USER_LEFT_CHANNELNAME, 'WARN_MODS_USER_LEFT', ' Warn channel not set', asyncCB);
});
// if (OPTIONS.WARN_MODS_USER_LEFT_CHANNELNAME !== undefined) {
// tipbot.getChannel(OPTIONS.WARN_MODS_USER_LEFT_CHANNELNAME, function (err, warnUserLeftChannel) {
// if (err) {
// debug('tipbot:bot')('ERROR: ' + OPTIONS.WARN_MODS_USER_LEFT_CHANNELNAME + ' channel not found!');
// } else {
// debug('tipbot:bot')('Init: channel ' + OPTIONS.WARN_MODS_USER_LEFT_CHANNELNAME + ' = ' + warnUserLeftChannel.id);
// // set new user warning channel for tipbot
// tipbot.OPTIONS.WARN_MODS_USER_LEFT = warnUserLeftChannel;
// }
// });
// }
// find channelID of MAIN_CHANNEL to post general messages
setChannelTasks.push(
function (asyncCB) {
setChannel(OPTIONS.MAIN_CHANNEL_NAME, 'MAIN_CHANNEL', 'No Main channel found to send general messages to', asyncCB);
});
// if (OPTIONS.MAIN_CHANNEL_NAME !== undefined) {
// tipbot.getChannel(OPTIONS.MAIN_CHANNEL_NAME, function (err, channel) {
// if (err) {
// debug('tipbot:bot')('ERROR: No Main channel found to send general messages to.');
// } else {
// debug('tipbot:bot')('Init: Main channel ' + OPTIONS.MAIN_CHANNEL_NAME + ' = ' + channel.id);
// // set moderator channel for tipbot
// tipbot.OPTIONS.MAIN_CHANNEL = channel;
// }
// });
// }
//execute all setChannel tasks
async.parallel(setChannelTasks,
function () {
debug('tipbot:init')('All channels are set.');
}
);
// connection is ready = clear initializing flag
initializing--;
// debug('tipbot:init')('Stop Hello, Init count is now ' + initializing);
});
function setChannel(channelName, tipBotChannel, errMsg, cb) {
// find channelID of MAIN_CHANNEL to post general messages
if (channelName !== undefined) {
tipbot.getChannel(channelName, function (err, channel) {
if (err) {
debug('tipbot:init')('ERROR: No ' + channelName + ' channel found. ' + errMsg);
cb();
} else {
tipbot.OPTIONS[tipBotChannel] = channel;
debug('tipbot:init')('Init: Channel ' + channelName + ' = ' + channel.id);
cb();
}
});
} else {
debug('tipbot:init')('ERROR: can get channel because channelname is not set');
cb();
}
}
// response to ticks
controller.on('tick', function () {
if (initializing === 0 && tipbot !== null && !tipbot.initializing) {
// only when TipBot is finished initializing
// check sun balance every X minutes
if (tipbot.OPTIONS.SUN_TIMER !== undefined &&
tipbot.sunUser !== undefined) {
// only check sun balance every SUN_TIMER min
if (sunTicker === 0) {
debug('tipbot:sun')('SUN: check balance > threshold now');
tipbot.checkForSun();
// reset ticker
sunTicker = tipbot.OPTIONS.SUN_TIMER * 60;
} else {
// decrease sunTicker until 0
sunTicker--;
}
}
// show random help command text every X minutes
if (OPTIONS.SHOW_RANDOM_HELP_TIMER !== undefined) {
// only check sun balance every SUN_TIMER min
if (helpTicker === 0) {
debug('tipbot:help')('Help ticker reached 0 : show random help text');
tipbot.showRandomHelp();
// reset ticker
helpTicker = OPTIONS.SHOW_RANDOM_HELP_TIMER * 60;
} else {
// decrease sunTicker until 0
helpTicker--;
}
}
} else if (initializing > 0) { debug('tipbot:init')('init counter ' + initializing); }
});
// emergency commands
controller.hears('emergency', ['direct_message'], function (bot, message) {
debug('tipbot:EMERGENCY')('**** Got this EMERGENCY message: ' + message.text);
bot.api.users.info({ 'user': message.user }, function (err, response) {
if (err) { throw new Error(err); }
let sender = response.user;
if (sender.is_admin === false) {
debug('tipbot:EMERGENCY')('Emergency used by non admin !');
} else {
debug('tipbot:EMERGENCY')('**** Emergency is authorised by: ' + sender.name);
if (message.text.match(/\brestart\b/i)) {
debug('tipbot:EMERGENCY')('**** Emergency connection restart ****');
if (initializing) {
debug('tipbot:EMERGENCY')('++++ Tried a restart while still initializing, restart aborted.');
} else { bot.closeRTM(); }
}
if (message.text.match(/\bstop\b/i)) {
debug('tipbot:EMERGENCY')('**** Emergency stop ****');
}
}
});
});
// listen to direct messages to the bot, or when the bot is mentioned in a message
controller.hears('.*', ['direct_message', 'direct_mention', 'mention'], function (bot, message) {
const member = message.user;
let channel;
if (tipbot === null) {
debug('tipbot:bot')('Problem: slack connection is up but tipbot isn\'t');
return;
}
// find the place where the message was posted
let firstCharOfChannelID = message.channel.substring(0, 1);
if (firstCharOfChannelID === 'C') {
// in Public channel
bot.api.channels.info({ 'channel': message.channel }, function (err, response) {
if (err) { throw new Error(err); }
channel = response.channel;
// let tipbot handle the message
tipbot.onMessage(channel, member, message.text);
});
} else if (firstCharOfChannelID === 'G') {
// in Private channel = Group
bot.api.groups.info({ 'channel': message.channel }, function (err, response) {
if (err) { throw new Error(err); }
channel = response.group;
// let tipbot handle the message
tipbot.onMessage(channel, member, message.text);
});
} else if (firstCharOfChannelID === 'D') {
// in Direct Message channel = id -> create channel object
// let tipbot handle the message
let DMchannelID = { 'id': message.channel };
tipbot.onMessage(DMchannelID, member, message.text);
}
// });
});
// when a user change his profile (other username,...)
controller.on('user_change', function (bot, resp) {
debug('tipbot:bot')('User ' + resp.user.name + ' has changed his/her profile.');
tipbot.onUserChange(bot, resp.user);
});
// when a new user joins the Slack Team to the user.id can be added
controller.on('team_join', function (bot, resp) {
debug('tipbot:bot')('User ' + resp.user.name + ' has joined !');
tipbot.onUserChange(bot, resp.user);
});