hubot-schedule-msg
Version:
Message scheduler for hubot, fit with RocketChat
458 lines (369 loc) • 14.3 kB
JavaScript
;
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
var _entries = require('babel-runtime/core-js/object/entries');
var _entries2 = _interopRequireDefault(_entries);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _job2 = require('./lib/job');
var _job3 = _interopRequireDefault(_job2);
var _config = require('./lib/config');
var _helpers = require('./lib/helpers');
var _helpers2 = _interopRequireDefault(_helpers);
var _dateTimeFormat = require('./lib/dateTimeFormat');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var mainRobot = function () {
function mainRobot(robot) {
(0, _classCallCheck3.default)(this, mainRobot);
this.robot = robot;
this.jobs = {};
this.isCronPattern = false;
}
(0, _createClass3.default)(mainRobot, [{
key: 'initial',
value: function initial() {
var _this = this;
this.robot.brain.on('loaded', function () {
return _this.syncSchedules();
});
// Set storage by key
if (!this.robot.brain.get(_config.AppConfig.storeKey)) {
this.robot.brain.set(_config.AppConfig.storeKey, {});
}
// hubot schedule <room|user>? "<time>" <message>
this.robot.respond(/schedule\s?((?:\@|\#|\m\e).*)?\s\"(.*)\" (.*)/i, this.respondNew.bind(this));
// hubot schedule list
this.robot.respond(/schedule list/i, this.respondList.bind(this));
// hubot cancel a schedule
this.robot.respond(/schedule (?:del|delete|remove|cancel) (\d+)/i, this.respondCancel.bind(this));
}
/**
* Response to the New schedule call
*
* @param msg
*/
}, {
key: 'respondNew',
value: function respondNew(msg) {
var target = msg.match[1];
// Check permission
if (target && _helpers2.default.isRestrictedRoom(target, this.robot, msg)) {
return msg.send(':warning: Creating schedule for the other room is restricted.');
}
var time = msg.match[2];
var message = msg.match[3];
// Start create schedule
return this.schedule(msg, target, time, message);
}
/**
* Response to the List schedule call
* @param msg
*/
}, {
key: 'respondList',
value: function respondList(msg) {
var userId = msg.message.user.id;
var userJobs = _helpers2.default.getJobByUser(this.jobs, userId);
// Create 2 collections of jobs by timePattern
var dateJobs = {};
var cronJobs = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)((0, _entries2.default)(userJobs)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = (0, _slicedToArray3.default)(_step.value, 2),
key = _step$value[0],
job = _step$value[1];
if (_helpers2.default.isTimeCronPattern(job.timePattern)) {
cronJobs[key] = job;
} else {
dateJobs[key] = job;
}
}
// Reorder jobs by date
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var dateJobsSorted = (0, _keys2.default)(dateJobs).sort(function (a, b) {
return new Date(dateJobs[a].timePattern) - new Date(dateJobs[b].timePattern);
});
var message = '';
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(dateJobsSorted), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var id = _step2.value;
var job = dateJobs[id];
message += '[*' + _helpers2.default.getJobId(id)[2] + '*] Send to ' + job.user.roomName + ': "' + job.message + '" at *' + job.timeOrigin + '*\n';
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)((0, _entries2.default)(cronJobs)), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _step3$value = (0, _slicedToArray3.default)(_step3.value, 2),
_id = _step3$value[0],
_job = _step3$value[1];
message += '[*' + _helpers2.default.getJobId(_id)[2] + '*] Send to ' + _job.user.roomName + ': "' + _job.message + '" by pattern *' + _job.timeOrigin + '*\n';
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
if (!message) {
message = 'No messages have been scheduled.';
}
var envelope = {
user: msg.message.user,
room: msg.message.user.room
};
return this.robot.adapter.sendDirect(envelope, message);
}
}, {
key: 'respondCancel',
value: function respondCancel(msg) {
var id = msg.message.user.id + '-' + msg.match[1];
var job = this.jobs[id];
if (!job) {
return this.errorHandling(msg.message.user, '*' + msg.match[1] + '*: Schedule not found.');
}
if (_helpers2.default.isRestrictedRoom(job.user.room, this.robot, msg)) {
return this.errorHandling(msg.message.user, 'Canceling schedule for the other room is restricted');
}
job.cancelSchedule();
delete this.jobs[id];
delete this.robot.brain.get(_config.AppConfig.storeKey)[id];
return msg.send('*' + msg.match[1] + '*: Schedule canceled.');
}
/**
* Prepare to create the message schedule
*
* @param msg
* @param target room name
* @param timePattern
* @param message
*/
}, {
key: 'schedule',
value: function schedule(msg, target, timePattern, message) {
// over maximum job waiting
var userJobs = _helpers2.default.getJobByUser(this.jobs, msg.message.user.id);
if (_config.AppConfig.jobMaximumPerUser < userJobs.length) {
return this.errorHandling(msg.message.user, ':warning: Too many scheduled messages.');
}
// Create job id
var id = null;
while (!id || this.jobs[id]) {
id = msg.message.user.id + '-' + Math.floor(Math.random() * _config.AppConfig.jobMaximumPerUser);
}
this.isCronPattern = _helpers2.default.isTimeCronPattern(timePattern);
try {
var job = this.scheduleCreate(id, timePattern, msg.message.user, target, message);
if (job) {
return msg.send(':white_check_mark: :hourglass_flowing_sand: [*' + _helpers2.default.getJobId(id)[2] + '*] Schedule created, trigger ' + (this.isCronPattern ? 'by pattern' : 'at:') + ' *' + timePattern + '*');
}
return msg.send(':warning: [*' + timePattern + '*] is invalid.');
} catch (e) {
return this.errorHandling(msg.message.user, e.message);
}
}
/**
* Create message schedule by timePattern type
*
* @param jobId
* @param timePattern
* @param user
* @param target room name
* @param message
*/
}, {
key: 'scheduleCreate',
value: function scheduleCreate(jobId, timePattern, user, target, message) {
var _this2 = this;
// Cron date pattern
if (this.isCronPattern) {
return this.scheduleStart(jobId, timePattern, timePattern, user, target, message);
}
// Normal date pattern schedule
var dateObj = (0, _dateTimeFormat.dateParse)(timePattern);
var dateTimestamp = dateObj.getTime();
if (!isNaN(dateTimestamp)) {
if (dateTimestamp < Date.now()) {
return this.errorHandling(user, ':warning: [*' + timePattern + '*] has already passed');
}
return this.scheduleStart(jobId, dateObj, timePattern, user, target, message, function () {
// Remove job after finished
delete _this2.jobs[jobId];
return delete _this2.robot.brain.get(_config.AppConfig.storeKey)[jobId];
});
}
}
/**
* Save schedule & kick off
*
* @param jobId
* @param time cron pattern / timestamp
* @param user
* @param target room name
* @param message
* @param callback
*/
}, {
key: 'scheduleStart',
value: function scheduleStart(jobId, time, timeOrigin, user, targetRoom, message, callback) {
// Get target room or current room
var room = targetRoom ? targetRoom : _helpers2.default.getRoomName(this.robot, user);
var job = new _job3.default(jobId, time, timeOrigin, user, room, message, callback);
job.start(this.robot);
this.jobs[jobId] = job;
return this.robot.brain.get(_config.AppConfig.storeKey)[jobId] = job.serialize();
}
}, {
key: 'syncSchedules',
value: function syncSchedules() {
if (!this.robot.brain.get(_config.AppConfig.storeKey)) {
this.robot.brain.set(_config.AppConfig.storeKey);
}
// sync jobs from brain storage to class
var nonCachedSchedules = _helpers2.default.compareObj(this.robot.brain.get(_config.AppConfig.storeKey), this.jobs);
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)((0, _entries2.default)(nonCachedSchedules)), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var _step4$value = (0, _slicedToArray3.default)(_step4.value, 2),
id = _step4$value[0],
job = _step4$value[1];
scheduleFromBrain(id, job.timeOrigin, job.user, job.message);
}
// sync jobs from class to brain storage
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
var results = [];
var nonStoredSchedules = _helpers2.default.compareObj(this.jobs, this.robot.brain.get(_config.AppConfig.storeKey));
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = (0, _getIterator3.default)((0, _entries2.default)(nonStoredSchedules)), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _step5$value = (0, _slicedToArray3.default)(_step5.value, 2),
id = _step5$value[0],
job = _step5$value[1];
results.push(storeScheduleInBrain(id, job));
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
return results;
}
}, {
key: 'scheduleFromBrain',
value: function scheduleFromBrain(jobId, timePattern, user, message) {
var envelope = { user: user, room: user.room };
var target = user.room;
try {
this.scheduleCreate(jobId, timePattern, user, target, message);
} catch (e) {
if (_config.envConfig.debug) {
return this.errorHandling(user, _helpers2.default.getJobId(jobId)[2] + ': Failed to sync schedule from brain [' + e.message + ']');
}
return delete this.robot.brain.get(_config.AppConfig.storeKey)[jobId];
}
if (_config.envConfig.debug) {
return this.robot.send(envelope, _helpers2.default.getJobId(jobId)[2] + ' scheduled from brain');
}
}
}, {
key: 'storeScheduleInBrain',
value: function storeScheduleInBrain(jobId, job) {
this.robot.brain.get(STORE_KEY)[jobId] = job.serialize();
var envelope = {
user: job.user,
room: job.user.room
};
if (config.debug === '1') {
return this.robot.send(envelope, _helpers2.default.getJobId(jobId)[2] + ': Schedule stored in brain asynchronously');
}
}
}, {
key: 'errorHandling',
value: function errorHandling(user, e) {
var envelope = { user: user };
if (_config.envConfig.errorEmit === '1') {
return this.robot.emit('error', e);
}
return this.robot.send(envelope, e);
}
}]);
return mainRobot;
}();
;
module.exports = function (robot) {
return new mainRobot(robot).initial();
};