diff --git a/modules/atheme/atheme.js b/modules/atheme/atheme.js new file mode 100644 index 0000000..9ab1502 --- /dev/null +++ b/modules/atheme/atheme.js @@ -0,0 +1,152 @@ +/** + * Module Name: atheme + * Description: atheme mode references & retrieve channel flags + */ +var _ = require('underscore')._, + async = require('async'); + +var atheme = function(dbot) { + this.flagStack = {}; + this.hostStack = {}; + + this.api = { + 'getChannelFlags': function(server, channel, callback) { + if(!_.has(this.flagStack, server)) this.flagStack[server] = {}; + if(_.has(this.flagStack[server], channel)) { // Already an active flag call + this.flagStack[server][channel].callbacks.push(callback); + } else { + this.flagStack[server][channel] = { + 'flags': {}, + 'callbacks': [ callback ] + }; + } + + dbot.say(server, 'chanserv', 'FLAGS ' + channel); + setTimeout(function() { // Delete callback if no response + if(_.has(this.flagStack[server], channel)) { + _.each(this.flagStack[server][channel].callbacks, function(callback) { + callback(true, null); + }); + delete this.flagStack[server][channel]; + } + }.bind(this), 20000); + }, + + 'getVHosts': function(server, mask, callback) { + if(!_.has(this.hostStack, server)) this.hostStack[server] = {}; + if(_.has(this.hostStack[server], mask)) { // Already an active host call + this.hostStack[server][channel].callbacks.push(callback); + } else { + this.hostStack[server][mask] = { + 'users': [], + 'callbacks': [ callback ] + }; + } + + dbot.say(server, 'hostserv', 'LISTVHOST ' + mask); + setTimeout(function() { // Delete callback if no response + if(_.has(this.hostStack[server], mask)) { + _.each(this.hostStack[server][mask].callbacks, function(callback) { + callback(true, null); + }); + delete this.hostStack[server][mask]; + } + }.bind(this), 5000); + } + }; + + this.commands = { + '~chanserv': function(event) { + if(_.has(this.config.chanserv, event.input[1])) { + event.reply('ChanServ flag ' + event.input[1] + ': ' + this.config.chanserv[event.input[1]]); + } else { + event.reply('I don\'t know anything about ' + event.input[1]); + } + }, + + '~chanmode': function(event) { + if(_.has(this.config.chanmodes, event.input[1])) { + event.reply('Channel Mode ' + event.input[1] + ': ' + this.config.chanmodes[event.input[1]]); + } else { + event.reply('I don\'t know anything about ' + event.input[1]); + } + } + }; + this.commands['~chanserv'].regex = [/^chanserv (\+.)/, 2]; + this.commands['~chanmode'].regex = [/^chanmode (\+.)/, 2]; + + this.listener = function(event) { + if(event.action === 'NOTICE') { + if(event.user === 'ChanServ') { + var flags = event.params.match(/(\d+)\s+([^ ]+)\s+(\+\w+)\s+\((\#[\w\.]+)\)/), + end = event.params.match(/end of \u0002(\#[\w\.]+)\u0002 flags listing/i); + + if(flags && _.has(this.flagStack[event.server], flags[4])) { + this.flagStack[event.server][flags[4]].flags[flags[2]] = flags[3]; + } else if(end) { + if(_.has(this.flagStack[event.server], end[1])) { + // Parse wildcard hostmasks to nicks + var allFlags = this.flagStack[event.server][end[1]].flags, + hostMasks = {}; + + _.each(allFlags, function(f, u) { // TODO: combine to one loop + if(u.indexOf('*!*@') !== -1) { + hostMasks[u] = f; + delete allFlags[u]; + } + }); + async.each(_.keys(hostMasks), function(hostMask, done) { + this.api.getVHosts(event.server, hostMask.split('@')[1], function(err, users) { + _.each(users, function(user) { + allFlags[user] = hostMasks[hostMask]; + }); + done(); + }); + }.bind(this), function() { + console.log('DONE'); + _.each(this.flagStack[event.server][end[1]].callbacks, function(callback) { + callback(null, this.flagStack[event.server][end[1]].flags); + }.bind(this)); + delete this.flagStack[event.server][end[1]]; + }.bind(this)); + } + } + } else if(event.user === 'HostServ') { + _.each(this.hostStack[event.server], function(el, mask) { + if(event.params.match(mask)) { + var user = event.params.match(/- ([^ ]+)/), + end = event.params.match(/matches for pattern/); + + if(user) { + this.hostStack[event.server][mask].users.push(user[1]); + } else if(end) { + _.each(this.hostStack[event.server][mask].callbacks, function(callback) { + callback(null, this.hostStack[event.server][mask].users); + }, this); + delete this.hostStack[event.server][mask]; + } + } + }, this); + } + } else { // PRIVMSG +console.log(event.message); + var akill = event.message.match(/([^ ]+) AKILL:ADD: ([^ ]+) \(reason: (.+)(\) )\(duration: ([^,)]+)/); + if(event.channel == '#services' && akill) { +console.log(akill); + var channel = dbot.config.servers[event.server].admin_channel; + dbot.api.users.getUser(akill[1] + '.' + event.server, function(err, user) { + dbot.api.report.notify('ban', 'tripsit', user, channel, dbot.t('akill', { + 'host': akill[2], + 'reason': akill[3], + 'duration': akill[5] + })); + }); + } + } + }.bind(this); + this.on = ['NOTICE', 'PRIVMSG']; +}; + +exports.fetch = function(dbot) { + return new atheme(dbot); +}; diff --git a/modules/charybdis/config.json b/modules/atheme/config.json similarity index 100% rename from modules/charybdis/config.json rename to modules/atheme/config.json diff --git a/modules/atheme/strings.json b/modules/atheme/strings.json new file mode 100644 index 0000000..bd4725a --- /dev/null +++ b/modules/atheme/strings.json @@ -0,0 +1,5 @@ +{ + "akill": { + "en": "{host} has been AKilled for {duration} due to \"{reason}\"" + } +} diff --git a/modules/charybdis/charybdis.js b/modules/charybdis/charybdis.js deleted file mode 100644 index 4e3f936..0000000 --- a/modules/charybdis/charybdis.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Module Name: charybdis - * Description: charybdis and atheme mode references - */ -var _ = require('underscore')._; - -var charybdis = function(dbot) { - this.commands = { - '~chanserv': function(event) { - if(_.has(this.config.chanserv, event.input[1])) { - event.reply('ChanServ flag ' + event.input[1] + ': ' + this.config.chanserv[event.input[1]]); - } else { - event.reply('I don\'t know anything about ' + event.input[1]); - } - }, - - '~chanmode': function(event) { - if(_.has(this.config.chanmodes, event.input[1])) { - event.reply('Channel Mode ' + event.input[1] + ': ' + this.config.chanmodes[event.input[1]]); - } else { - event.reply('I don\'t know anything about ' + event.input[1]); - } - } - }; - this.commands['~chanserv'].regex = [/^chanserv (\+.)/, 2] - this.commands['~chanmode'].regex = [/^chanmode (\+.)/, 2] -}; - -exports.fetch = function(dbot) { - return new charybdis(dbot); -}; diff --git a/modules/command/commands.js b/modules/command/commands.js index b5860cc..d5d5bbb 100644 --- a/modules/command/commands.js +++ b/modules/command/commands.js @@ -2,7 +2,7 @@ var _ = require('underscore')._, request = require('request'); var commands = function(dbot) { - return { + var commands = { 'usage': function(event) { var commandName = event.params[1]; if(_.has(dbot.usage, commandName)) { @@ -60,6 +60,9 @@ var commands = function(dbot) { } } }; + commands['usage'].regex = [/usage ([^ ]+)/, 2]; + + return commands; }; exports.fetch = function(dbot) { diff --git a/modules/crypto/usage.json b/modules/crypto/usage.json new file mode 100644 index 0000000..bbec8f2 --- /dev/null +++ b/modules/crypto/usage.json @@ -0,0 +1,6 @@ +{ + "~md5": "~md5 [text]", + "~sha1": "~sha1 [text]", + "~sha256": "~sha256 [text]", + "~aes": "~aes \"[text]\" \"[key]\"" +} diff --git a/modules/ignore/ignore.js b/modules/ignore/ignore.js index 46c43f9..8b25814 100644 --- a/modules/ignore/ignore.js +++ b/modules/ignore/ignore.js @@ -16,7 +16,7 @@ var ignore = function(dbot) { this.api.getUserIgnores(user, function(err, ignores) { var isImpeded = false; if(!err && ignores) { - if(_.has(dbot.commands, item)) { + if(_.has(dbot.commands, item) && !_.include(ignores[by], item)) { item = dbot.commands[item].module; } if(_.include(ignores[by], item)) { @@ -273,7 +273,7 @@ var ignore = function(dbot) { dbot.instance.clearIgnores(); this.db.scan('ignores', function(ignores) { - dbot.api.users.getUser(ignores.id, function(user) { + dbot.api.users.getUser(ignores.id, function(err, user) { if(user) { _.each(ignores.ignores, function(module) { dbot.instance.ignoreTag(user.currentNick, module); diff --git a/modules/kick/commands.js b/modules/kick/commands.js index 6621e2e..577cc58 100644 --- a/modules/kick/commands.js +++ b/modules/kick/commands.js @@ -28,7 +28,7 @@ var commands = function(dbot) { this.api.voice(server, quietee, channel); } - dbot.api.users.resolveUser(server, dbot.config.name, function(user) { + dbot.api.users.resolveUser(server, dbot.config.name, function(err, user) { dbot.api.report.notify('unquiet', server, user, channel, dbot.t('unquiet_notify', { 'unquieter': dbot.config.name, diff --git a/modules/lastfm/lastfm.js b/modules/lastfm/lastfm.js index 489e88c..e0ab247 100644 --- a/modules/lastfm/lastfm.js +++ b/modules/lastfm/lastfm.js @@ -15,7 +15,7 @@ var lastfm = function(dbot) { 'getLastFM': function(server, nick, callback) { dbot.api.profile.getProfile(server, nick, function(err, user, profile) { if(user) { - if(profile && _.has(profile.profile, 'lastfm')) { + if(profile && _.has(profile.profile, 'lastfm') && _.isString(profile.profile.lastfm)) { callback(user, profile.profile.lastfm.toLowerCase()); } else { callback(user, null); @@ -187,7 +187,7 @@ var lastfm = function(dbot) { .value(); async.each(scrobbliest, function(item, done) { - dbot.api.users.getUser(item.user, function(user) { + dbot.api.users.getUser(item.user, function(err, user) { item.user = user; done(); }); @@ -327,9 +327,9 @@ var lastfm = function(dbot) { async.each(tastiest, function(pair, done) { if(!_.isObject(pair.p1)) { // fix this - dbot.api.users.getUser(pair.p1, function(user) { + dbot.api.users.getUser(pair.p1, function(err, user) { pair.p1 = user; - dbot.api.users.getUser(pair.p2, function(user) { + dbot.api.users.getUser(pair.p2, function(err, user) { pair.p2 = user; done(); }); diff --git a/modules/leafly/config.json b/modules/leafly/config.json new file mode 100644 index 0000000..8cbabb5 --- /dev/null +++ b/modules/leafly/config.json @@ -0,0 +1,5 @@ +{ + "outputPrefix": "\u00033weed\u000f", + "app_key": "", + "app_id": "" +} diff --git a/modules/leafly/leafly.js b/modules/leafly/leafly.js new file mode 100644 index 0000000..22c4929 --- /dev/null +++ b/modules/leafly/leafly.js @@ -0,0 +1,47 @@ +/** + * Module name: Leafly + * Description: Information from leafly + */ + +var _ = require('underscore')._, + request = require('request'); + +var leafly = function(dbot) { + var ApiRoot = 'http://data.leafly.com/'; + + this.commands = { + '~strain': function(event) { + request.post(ApiRoot + 'strains', { + 'headers': { + 'app_key': this.config.app_key, + 'app_id': this.config.app_id + }, + 'body': { + 'page': 0, + 'take': 1, + 'sort': 'rating', + 'search': event.input[1] + }, + 'json': true + }, function(error, response, body) { + if(_.isObject(body) && _.has(body, 'Strains') && body.Strains.length > 0) { + var strain = body.Strains[0], + flavours = _.pluck(strain.Flavors, 'Name').join(', '); + + event.reply(dbot.t('strain', { + 'name': strain.Name, + 'flavours': flavours, + 'link': strain.permalink + })); + } else { + event.reply(dbot.t('no_strains')); + } + }.bind(this)); + } + }; + this.commands['~strain'].regex = [/^strain (.+)$/, 2]; +}; + +exports.fetch = function(dbot) { + return new leafly(dbot); +}; diff --git a/modules/leafly/strings.json b/modules/leafly/strings.json new file mode 100644 index 0000000..9494b80 --- /dev/null +++ b/modules/leafly/strings.json @@ -0,0 +1,8 @@ +{ + "strain": { + "en": "{name} tastes of {flavours} - {link}" + }, + "no_strains": { + "en": "No strains found :(" + } +} diff --git a/modules/poll/pages.js b/modules/poll/pages.js index a4df4db..b3b77cc 100644 --- a/modules/poll/pages.js +++ b/modules/poll/pages.js @@ -15,7 +15,7 @@ var pages = function(dbot) { var voterNicks = []; async.each(_.keys(poll.votees), function(id, done) { - dbot.api.users.getUser(id, function(user) { + dbot.api.users.getUser(id, function(err, user) { voterNicks.push(user.primaryNick); done(); }); diff --git a/modules/profile/api.js b/modules/profile/api.js index 9324270..781ce12 100644 --- a/modules/profile/api.js +++ b/modules/profile/api.js @@ -9,16 +9,13 @@ var api = function(dbot) { * TODO(@samstudio8) Migrate to internalAPI */ "createProfile": function(user, callback){ + if(!callback) callback = function(){}; if(user){ this.db.create('profiles', user.id, { 'id': user.id, 'profile': this.config.schema.profile, 'preferences': this.config.schema.preferences - }, function(err, result){ - if(err){ - console.log(err); - } - }); + }, callback); } }, @@ -28,15 +25,21 @@ var api = function(dbot) { }, 'getProfile': function(server, nick, callback){ - dbot.api.users.resolveUser(server, nick, function(user){ + dbot.api.users.resolveUser(server, nick, function(err, user){ if(user){ this.db.read('profiles', user.id, function(err, profile){ if(!err){ - callback(false, user, profile); + if(profile) { + callback(false, user, profile); + } else { + this.api.createProfile(user, function(err, profile) { + callback(null, user, profile); + }); + } } else { callback(true, user, null); } - }); + }.bind(this)); } else{ callback(true, null, null); @@ -44,10 +47,16 @@ var api = function(dbot) { }.bind(this)); }, - 'getProfileByUUID': function(uuid, callback){ - this.db.read('profiles', uuid, function(err, profile){ - callback(profile); - }); + 'getProfileByUser': function(user, callback){ + this.db.read('profiles', user.id, function(err, profile){ + if(profile) { + callback(profile); + } else { + this.api.createProfile(user, function(err, profile) { + callback(profile); + }); + } + }.bind(this)); }, 'getAllProfiles': function(callback){ diff --git a/modules/profile/pages.js b/modules/profile/pages.js index 73b4c08..309b36e 100644 --- a/modules/profile/pages.js +++ b/modules/profile/pages.js @@ -7,7 +7,7 @@ var pages = function(dbot) { var connection = req.params.connection; var nick = req.params.user; - dbot.api.users.resolveUser(connection, nick, function(user){ + dbot.api.users.resolveUser(connection, nick, function(err, user){ if(user){ dbot.api.profile.getProfile(connection, user.primaryNick, function(err, user, profile){ if(!err){ @@ -45,7 +45,7 @@ var pages = function(dbot) { dbot.api.profile.getAllProfiles(function(profiles){ var thumbnails = []; _.each(profiles, function(profile){ - var nick = dbot.api.users.getUser(profile.id, function(user){ + var nick = dbot.api.users.getUser(profile.id, function(err, user){ if(user){ /*TODO(@tmenari / @samstudio8) diff --git a/modules/profile/profile.js b/modules/profile/profile.js index c2f264c..9b9a6ef 100644 --- a/modules/profile/profile.js +++ b/modules/profile/profile.js @@ -5,30 +5,16 @@ var profile = function(dbot) { this.onLoad = function(){ var schema = this.config.schema; - // Ensure all users have a profile - dbot.api.users.getAllUsers(function(users){ - if(users){ - _.each(users, function(user){ - this.api.getProfileByUUID(user.id, function(err, uuid, profile){ - // If function returns an error and uuid, create a new profile - if(err && uuid){ - this.api.createProfile(user); - } - }.bind(this)); - }.bind(this)); - } - }.bind(this)); - // Add API Hooks dbot.api.event.addHook('new_user', this.api.createProfile); dbot.instance.addPreEmitHook(function(event, callback) { if(!event.rUser) return callback(); - this.api.getProfileByUUID(event.rUser.id, function(uProfile) { + this.api.getProfileByUser(event.rUser, function(uProfile) { if(uProfile) { event.rProfile = uProfile.profile; - callback(); } + callback(); }.bind(this)); }.bind(this)); diff --git a/modules/quotes/commands.js b/modules/quotes/commands.js index 3821a82..8448ba2 100644 --- a/modules/quotes/commands.js +++ b/modules/quotes/commands.js @@ -14,11 +14,18 @@ var commands = function(dbot) { this.api.addQuote(key, quote, event.user, function(newCount) { if(newCount) { dbot.api.event.emit('~qadd', [ key, quote ]); - event.reply(dbot.t('quote_saved', { - 'category': key, - 'count': newCount, - 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) - })); + if(_.has(dbot.modules, 'web')) { + event.reply(dbot.t('quote_saved', { + 'category': key, + 'count': newCount, + 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) + })); + } else { + event.reply(dbot.t('quote_saved', { + 'category': key, + 'count': newCount + })); + } } else { event.reply(dbot.t('quote_exists')); } @@ -103,11 +110,18 @@ var commands = function(dbot) { removedQuote; var quoteRemoved = function(err) { this.internalAPI.resetRemoveTimer(event, key, removedQuote); - event.reply(dbot.t('removed_from', { - 'quote': removedQuote, - 'category': key, - 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) - })); + if(_.has(dbot.modules, 'web')) { + event.reply(dbot.t('removed_from', { + 'quote': removedQuote, + 'category': key, + 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) + })); + } else { + event.reply(dbot.t('removed_from', { + 'quote': removedQuote, + 'category': key + })); + } }.bind(this); this.db.search('quote_category', { 'name': key }, function(result) { @@ -137,11 +151,18 @@ var commands = function(dbot) { category = false; var quoteRemoved = function(err) { this.internalAPI.resetRemoveTimer(event, key, quote); - event.reply(dbot.t('removed_from', { - 'category': key, - 'quote': quote, - 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) - })); + if(_.has(dbot.modules, 'web')) { + event.reply(dbot.t('removed_from', { + 'category': key, + 'quote': quote, + 'link': dbot.api.web.getUrl('quotes/' + encodeURIComponent(key)) + })); + } else { + event.reply(dbot.t('removed_from', { + 'category': key, + 'quote': quote + })); + } }.bind(this); this.db.search('quote_category', { 'name': key }, function(result) { diff --git a/modules/quotes/quotes.js b/modules/quotes/quotes.js index 004d4a0..66d565f 100644 --- a/modules/quotes/quotes.js +++ b/modules/quotes/quotes.js @@ -16,11 +16,14 @@ var quotes = function(dbot) { if(quoteRefs) { var ref = this.internalAPI.cleanRef(quoteRefs[0].replace(/^~~/,'').replace(/~~$/,'').trim()); if(ref === '-nicks-') { - dbot.api.users.getRandomChannelUser(server, channel, function(user) { - quote = quote.replace('~~' + ref + '~~', user); + if(_.has(dbot.instance.connections[server].channels, channel)) { + var nicks = + _.keys(dbot.instance.connections[server].channels[channel].nicks); + var randomUser = nicks[_.random(0, nicks.length -1)]; + quote = quote.replace('~~' + ref + '~~', randomUser); this.internalAPI.interpolatedQuote(server, channel, username, key, quote, callback); - }.bind(this)); + } } else if(ref === '-nick-') { quote = quote.replace('~~' + ref + '~~', username); this.internalAPI.interpolatedQuote(server, channel, diff --git a/modules/quotes/strings.json b/modules/quotes/strings.json index b489fc0..9feaaa2 100644 --- a/modules/quotes/strings.json +++ b/modules/quotes/strings.json @@ -1,6 +1,6 @@ { "category_not_found": { - "en": "Nobody loves {category}", + "en": "Everyone is rather ambivalent towards the matter of {category}", "es": "Nadie ama a {category}", "na'vi": "{category} yawne ke lu kawturu.", "cy": "Does neb yn caru {category}", diff --git a/modules/radio/config.json b/modules/radio/config.json index 2eeff65..03ca8b9 100644 --- a/modules/radio/config.json +++ b/modules/radio/config.json @@ -1,5 +1,5 @@ { - "stream": "http://tripsit.fm:8000", - "announce": [ { "server": "tripsit", "name": "#tripbot" } ], + "stream": "http://radio.tripsit.me:8000/tripradio", + "announce": [ { "server": "tripsit", "name": "#drugs" } ], "outputPrefix": "\u00033radio\u000f" } diff --git a/modules/radio/radio.js b/modules/radio/radio.js index e4ad617..d2239c0 100644 --- a/modules/radio/radio.js +++ b/modules/radio/radio.js @@ -21,18 +21,20 @@ var radio = function(dbot) { stream.on('response', function(res) { this.data = res.headers; - _.each(this.config.announce, function(a) { - dbot.say(a.server, a.name, dbot.t('now_online', { - 'name': res.headers['icy-name'], - 'desc': res.headers['icy-description'], - 'url': res.headers['icy-url'] - })); - }); + if(res.headers['icy-name']) { + _.each(this.config.announce, function(a) { + dbot.say(a.server, a.name, dbot.t('now_online', { + 'name': res.headers['icy-name'], + 'desc': res.headers['icy-description'], + 'url': res.headers['icy-url'] + })); + }); + } }.bind(this)); stream.on('metadata', function(metadata) { var title = icecast.parseMetadata(metadata).StreamTitle; - if(title != 'undefined') { // sowwy jesus + if(!_.isUndefined(title) && this.data['icy-name']) { // sowwy jesus _.each(this.config.announce, function(a) { dbot.say(a.server, a.name, dbot.t('now_playing', { 'name': this.data['icy-name'], @@ -54,7 +56,7 @@ var radio = function(dbot) { var dj = this.data['icy-description'], song = event.input[1]; - dbot.api.users.resolveUser(event.server, dj, function(user) { + dbot.api.users.resolveUser(event.server, dj, function(err, user) { if(user) { dbot.say(event.server, user.currentNick, dbot.t('radio_request',{ 'user': event.user, diff --git a/modules/report/api.js b/modules/report/api.js index a14f905..5e5733a 100644 --- a/modules/report/api.js +++ b/modules/report/api.js @@ -24,56 +24,100 @@ var api = function(dbot) { }); var channel = dbot.instance.connections[server].channels[cName]; - var ops = _.filter(channel.nicks, function(user) { - if(this.config.notifyVoice) { - return user.op || user.voice; - } else { - return user.op; - } - }, this); - ops = _.pluck(ops, 'name'); - - dbot.api.users.resolveChannel(server, cName, function(channel) { - if(channel) { - var perOps = channel.op; - if(this.config.notifyVoice) perOps = _.union(perOps, channel.voice); - - this.db.read('nunsubs', channel.id, function(err, nunsubs) { - async.eachSeries(ops, function(nick, next) { - dbot.api.users.resolveUser(server, nick, function(user) { - if(!_.include(user.mobile, user.currentNick)) { - perOps = _.without(perOps, user.id); + if(_.has(dbot.modules, 'atheme')) { + dbot.api.atheme.getChannelFlags(server, cName, function(err, flags) { + var ops = _.map(flags, function(f, k) { + var staff = (f.indexOf('O') !== -1); + if(this.config.notifyVoice && !staff) { + staff = (f.indexOf('V') !== -1); + } + if(staff) { + return k; + } + }.bind(this)); + ops = _.without(ops, undefined); +console.log(ops); + this.db.read('nunsubs', cName + '.' + server, function(err, nunsubs) { + if(nunsubs) { + _.each(nunsubs.users, function(user) { + var uPart = user.split('.')[0]; + if(_.include(ops, uPart)) { +console.log('removing ' + uPart); + ops = _.without(ops, uPart); } - if(nunsubs && _.include(nunsubs.users, user.id)) { - ops = _.without(ops, user.currentNick); + }); + } + + var offlineOps = {}; + async.each(ops, function(op, done) { + dbot.api.users.isOnline(server, cName, op, function(err, user, online) { + if(!err && !online) offlineOps[op] = user; + if(user.currentNick !== op) { + ops = _.without(ops, op); + ops.push(user.currentNick); } - next(); - }); + done(); + }); }, function() { - offlineUsers = perOps; - if(!_.include(this.config.noMissingChans, cName)) { - _.each(offlineUsers, function(id) { - if(!this.pending[id]) this.pending[id] = []; - this.pending[id].push({ - 'time': new Date().getTime(), - 'channel': cName, - 'user': user.primaryNick, - 'message': message - }); - this.pNotify[id] = true; - }.bind(this)); - } - + // Queue notifies for offline ops + _.each(offlineOps, function(op) { + if(!this.pending[op.id]) this.pending[op.id] = []; + this.pending[op.id].push({ + 'time': new Date().getTime(), + 'channel': cName, + 'user': user.id, + 'message': message + }); + this.pNotify[op.id] = true; + }, this); + + // Send notifies to online ops + ops = _.difference(ops, _.keys(offlineOps)); message = this.internalAPI.formatNotify(type, server, - user, cName, message); + user, cName, message); this.internalAPI.notify(server, ops, message); if(_.has(this.config.chan_redirs, cName)) { dbot.say(server, this.config.chan_redirs[cName], message); } - }.bind(this)); + }.bind(this)); }.bind(this)); - } - }.bind(this)); + }.bind(this)); + } else { + var channel = dbot.instance.connections[server].channels[cName]; + var ops = _.filter(channel.nicks, function(user) { + if(this.config.notifyVoice) { + return user.op || user.voice; + } else { + return user.op; + } + }, this); + ops = _.pluck(ops, 'name'); + + dbot.api.users.resolveChannel(server, cName, function(channel) { + if(channel) { + var perOps = channel.op; + if(this.config.notifyVoice) perOps = _.union(perOps, channel.voice); + + this.db.read('nunsubs', cName + '.' + server, function(err, nunsubs) { + async.eachSeries(ops, function(nick, next) { + dbot.api.users.resolveUser(server, nick, function(user) { + if(nunsubs && _.include(nunsubs.users, user.id)) { + ops = _.without(ops, user.currentNick); + } + next(); + }); + }, function() { + message = this.internalAPI.formatNotify(type, server, + user, cName, message); + this.internalAPI.notify(server, ops, message); + if(_.has(this.config.chan_redirs, cName)) { + dbot.say(server, this.config.chan_redirs[cName], message); + } + }.bind(this)); + }.bind(this)); + } + }.bind(this)); + } }, 'notifyUsers': function(server, users, message) { diff --git a/modules/report/commands.js b/modules/report/commands.js index b966a24..d5d28e7 100644 --- a/modules/report/commands.js +++ b/modules/report/commands.js @@ -75,9 +75,10 @@ var commands = function(dbot) { }, '~report': function(event) { - var channelName = (event.input[1].trim() || event.channel), + var channelName = (event.input[1] || event.channel.toString()), nick = event.input[2], reason = event.input[3].trim(); + channelName = channelName.trim(); if(channelName == event.user) { channelName = dbot.config.servers[event.server].admin_channel; @@ -85,7 +86,7 @@ var commands = function(dbot) { if(reason.charAt(reason.length - 1) != '.') reason += '.'; - dbot.api.users.resolveUser(event.server, nick, function(reportee) { + dbot.api.users.resolveUser(event.server, nick, function(err, reportee) { if(_.has(event.allChannels, channelName)) { if(reportee) { this.api.notify('report', event.server, event.rUser, @@ -114,7 +115,7 @@ var commands = function(dbot) { if(_.has(event.allChannels, channelName)) { if(this.config.firstHost) { var first = message.split(' ')[0]; - dbot.api.users.resolveUser(event.server, first, function(user) { + dbot.api.users.resolveUser(event.server, first, function(err, user) { if(user && _.include(this.config.host_lookup, channelName)) { dbot.api.nickserv.getUserHost(event.server, first, function(host) { message = message.replace(first, first + ' [' + host + ']'); @@ -138,56 +139,54 @@ var commands = function(dbot) { }, '~nunsub': function(event) { - var cName = event.input[1]; + var cName = event.input[1], + cId = event.input[1] + '.' + event.server; - dbot.api.users.resolveChannel(event.server, cName, function(channel) { - if(channel) { - this.db.read('nunsubs', channel.id, function(err, nunsubs) { - if(!nunsubs) { - var nunsubs = { - 'id': channel.id, - 'users': [] - } - } - - if(!_.include(nunsubs, event.rUser.id)) { - nunsubs.users.push(event.rUser.id); - this.db.save('nunsubs', channel.id, nunsubs, function() { - var reply = dbot.t('nunsubbed', { 'cName': cName }) - if(_.has(this.config.chan_redirs, cName)) { - reply += dbot.t('n_also_found', { 'afaName' : this.config.chan_redirs[cName] }); - } - event.reply(reply); - }.bind(this)); - } else { - event.reply(dbot.t('already_nunsubbed', { 'cName': cName })); + if(_.has(dbot.instance.connections[event.server].channels, cName)) { + this.db.read('nunsubs', cId, function(err, nunsubs) { + if(!nunsubs) { + var nunsubs = { + 'id': cId, + 'users': [] } - }.bind(this)); - } else { - event.reply('Channel not known.'); - } - }.bind(this)); + } + + if(!_.include(nunsubs, event.rUser.id)) { + nunsubs.users.push(event.rUser.id); + this.db.save('nunsubs', cId, nunsubs, function() { + var reply = dbot.t('nunsubbed', { 'cName': cName }) + if(_.has(this.config.chan_redirs, cName)) { + reply += dbot.t('n_also_found', { 'afaName' : this.config.chan_redirs[cName] }); + } + event.reply(reply); + }.bind(this)); + } else { + event.reply(dbot.t('already_nunsubbed', { 'cName': cName })); + } + }.bind(this)); + } else { + event.reply('Channel not known.'); + } }, '~ununsub': function(event) { - var cName = event.input[1]; + var cName = event.input[1], + cId = event.input[1] + '.' + event.server; - dbot.api.users.resolveChannel(event.server, cName, function(channel) { - if(channel) { - this.db.read('nunsubs', channel.id, function(err, nunsubs) { - if(!_.isUndefined(nunsubs) && _.include(nunsubs.users, event.rUser.id)) { - nunsubs.users = _.without(nunsubs.users, event.rUser.id); - this.db.save('nunsubs', channel.id, nunsubs, function() { - event.reply(dbot.t('ununsubbed', { 'cName': cName })); - }); - } else { - event.reply(dbot.t('not_nunsubbed', { 'cName': cName })); - } - }.bind(this)); - } else { - event.reply('Channel not known.'); - } - }.bind(this)); + if(_.has(dbot.instance.connections[event.server].channels, cName)) { + this.db.read('nunsubs', cId, function(err, nunsubs) { + if(!_.isUndefined(nunsubs) && _.include(nunsubs.users, event.rUser.id)) { + nunsubs.users = _.without(nunsubs.users, event.rUser.id); + this.db.save('nunsubs', cId, nunsubs, function() { + event.reply(dbot.t('ununsubbed', { 'cName': cName })); + }); + } else { + event.reply(dbot.t('not_nunsubbed', { 'cName': cName })); + } + }.bind(this)); + } else { + event.reply('Channel not known.'); + } } }; commands['~report'].regex = /^report (#[^ ]+ )?([^ ]+) (.*)$/; diff --git a/modules/report/pages.js b/modules/report/pages.js index 3c17030..07d814f 100644 --- a/modules/report/pages.js +++ b/modules/report/pages.js @@ -33,7 +33,7 @@ var pages = function(dbot) { }); async.eachSeries(userCount, function(userCount, next) { - dbot.api.users.getUser(userCount.id, function(user) { + dbot.api.users.getUser(userCount.id, function(err, user) { if(user) { userCount['name'] = user.primaryNick; users.push(userCount); @@ -72,7 +72,7 @@ var pages = function(dbot) { }); }.bind(this), function() { async.each(notifications, function(notify, done) { - dbot.api.users.getUser(notify.user, function(user) { + dbot.api.users.getUser(notify.user, function(err, user) { if(user) notify.user = user.primaryNick; done(); }); @@ -138,7 +138,7 @@ var pages = function(dbot) { var pNickCache = {}; async.eachSeries(notifies, function(notify, next) { if(!_.has(pNickCache, notify.user)) { - dbot.api.users.getUser(notify.user, function(user) { + dbot.api.users.getUser(notify.user, function(err, user) { pNickCache[notify.user] = user.primaryNick; notify.user = user.primaryNick; next(); @@ -163,7 +163,7 @@ var pages = function(dbot) { } else { var username = req.params.item; - dbot.api.users.resolveUser(server, username, function(user) { + dbot.api.users.resolveUser(server, username, function(err, user) { this.db.search('notifies', { 'user': user.id }, function(notify) { diff --git a/modules/report/report.js b/modules/report/report.js index 94f634e..2f29b0b 100644 --- a/modules/report/report.js +++ b/modules/report/report.js @@ -10,6 +10,7 @@ var report = function(dbot) { this.internalAPI = { 'notify': function(server, users, message) { async.eachSeries(users, function(nick, next) { +console.log('sending msg to ' + nick); setTimeout(function() { dbot.say(server, nick, message); next(); diff --git a/modules/sstats/commands.js b/modules/sstats/commands.js index 7176198..356eab3 100644 --- a/modules/sstats/commands.js +++ b/modules/sstats/commands.js @@ -5,46 +5,41 @@ var _ = require('underscore')._, var commands = function(dbot) { var commands = { '~words': function(event) { - var getWords = function(user) { - this.api.getUserStats(user.id, function(uStats) { - if(uStats) { - var output = dbot.t('sstats_uwords', { - 'user': user.primaryNick, - 'words': uStats.words, - 'curses': uStats.curses, - 'capitals': uStats.capitals, - 'date': moment(uStats.creation).format('DD/MM/YYYY') - }); - if(event.rChannel && _.has(uStats.channels, event.rChannel.id)) { - ucStats = uStats.channels[event.rChannel.id]; - output += dbot.t('sstats_ucwords', { - 'channel': event.channel, - 'words': ucStats.words, - 'curses': ucStats.curses, - 'capitals': ucStats.capitals - }); - } - event.reply(output); - } else { - event.reply(dbot.t('sstats_noustats')); - } - }); - }.bind(this); - + var id = event.rUser.primaryNick + '.' + event.server, + user = event.rUser.currentNick, + cId = event.channel + '.' + event.server; if(event.params[1]) { - dbot.api.users.resolveUser(event.server, event.params[1], function(user) { - if(user) { - getWords(user); - } else { - event.reply(dbot.t('sstats_unknown_user')); - } - }); - } else { - getWords(event.rUser); + id = event.params[1] + '.' + event.server; + user = event.params[1]; } + + this.api.getUserStats(id, function(uStats) { + if(uStats) { + var output = dbot.t('sstats_uwords', { + 'user': user, + 'words': uStats.words, + 'curses': uStats.curses, + 'capitals': uStats.capitals, + 'date': moment(uStats.creation).format('DD/MM/YYYY') + }); + if(_.has(uStats.channels, cId)) { + ucStats = uStats.channels[cId]; + output += dbot.t('sstats_ucwords', { + 'channel': event.channel, + 'words': ucStats.words, + 'curses': ucStats.curses, + 'capitals': ucStats.capitals + }); + } + event.reply(output); + } else { + event.reply(dbot.t('sstats_noustats')); + } + }); }, '~lines': function(event) { + var cId = event.channel + '.' + event.server; var getLines = function(user) { this.api.getUserStats(user.id, function(uStats) { if(uStats) { @@ -53,10 +48,10 @@ var commands = function(dbot) { 'lines': uStats.lines, 'date': moment(uStats.creation).format('DD/MM/YYYY') }); - if(event.rChannel && _.has(uStats.channels, event.rChannel.id)) { + if(_.has(uStats.channels, cId)) { output += dbot.t('sstats_uclines', { 'channel': event.channel, - 'lines': uStats.channels[event.rChannel.id].lines + 'lines': uStats.channels[cId].lines }); } event.reply(output); @@ -160,7 +155,7 @@ var commands = function(dbot) { }, '~last': function(event) { - dbot.api.users.resolveUser(event.server, event.params[1], function(user) { + dbot.api.users.resolveUser(event.server, event.params[1], function(err, user) { if(user) { this.api.getUserStats(user.id, function(uStats) { if(uStats && uStats.last) { @@ -218,7 +213,7 @@ var commands = function(dbot) { .value(); async.eachSeries(pCounts, function(pCount, next) { - dbot.api.users.getUser(pCount[0], function(user) { + dbot.api.users.getUser(pCount[0], function(err, user) { pCount[0] = user.primaryNick; next(); }); }, function() { diff --git a/modules/sstats/sstats.js b/modules/sstats/sstats.js index cc9e70b..71a4ac8 100644 --- a/modules/sstats/sstats.js +++ b/modules/sstats/sstats.js @@ -20,7 +20,10 @@ var sstats = function(dbot) { // repetition 'highscore': function(key, property, callback) { var pList = {}, + pPointer = property; + if(!_.isArray(pPointer)) { pPointer = property.split('.'); + } this.db.scan(key, function(item) { var id = item.id; @@ -44,7 +47,7 @@ var sstats = function(dbot) { .value(); async.eachSeries(pCounts, function(pCount, next) { - dbot.api.users.getUser(pCount[0], function(user) { + dbot.api.users.getUser(pCount[0], function(err, user) { pCount[0] = user.primaryNick; next(); }); }, function() { @@ -54,14 +57,9 @@ var sstats = function(dbot) { }.bind(this), 'channelHighscore': function(key, server, channel, property, callback) { - dbot.api.users.resolveChannel(server, channel, function(channel) { - if(channel) { - var newProperty = 'channels.' + channel.id + '.' + property; - this.internalAPI.highscore(key, newProperty, callback); - } else { - callback(null); - } - }.bind(this)); + var cId = channel + '.' + server; + var newProperty = ['channels', cId, property]; + this.internalAPI.highscore(key, newProperty, callback); }.bind(this), 'formatHighscore': function(string, pCounts) { @@ -78,6 +76,7 @@ var sstats = function(dbot) { event.uStats.lines++; var message = event.message.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, ""); + var cId = event.channel + '.' + event.server; var words = message.split(' '), wCount = words.length, capitals = 0, @@ -98,18 +97,18 @@ var sstats = function(dbot) { event.cStats.capitals += capitals; event.cStats.curses += curses; - if(!_.has(event.uStats.channels, event.rChannel.id)) { - event.uStats.channels[event.rChannel.id] = { + if(!_.has(event.uStats.channels, cId)) { + event.uStats.channels[cId] = { 'lines': 1, 'words': wCount, 'capitals': capitals, 'curses': curses }; } else { - event.uStats.channels[event.rChannel.id].lines++; - event.uStats.channels[event.rChannel.id].words += wCount; - event.uStats.channels[event.rChannel.id].capitals += capitals; - event.uStats.channels[event.rChannel.id].curses += curses; + event.uStats.channels[cId].lines++; + event.uStats.channels[cId].words += wCount; + event.uStats.channels[cId].capitals += capitals; + event.uStats.channels[cId].curses += curses; } // Look for tracked words. @@ -124,9 +123,9 @@ var sstats = function(dbot) { this.api.getTrackedWord(word, function(tWord) { if(tWord) { tWord.total += count; - if(!_.has(tWord.channels, event.rChannel.id)) tWord.channels[event.rChannel.id] = 0; + if(!_.has(tWord.channels, cId)) tWord.channels[cId] = 0; if(!_.has(tWord.users, event.rUser.id)) tWord.users[event.rUser.id] = 0; - tWord.channels[event.rChannel.id] += count; + tWord.channels[cId] += count; tWord.users[event.rUser.id] += count; this.db.save('tracked_words', word, tWord, function() {}); } @@ -158,13 +157,14 @@ var sstats = function(dbot) { // Preload channel stats dbot.instance.addPreEmitHook(function(event, callback) { - if(!event.rChannel) return callback(); - this.api.getChannelStats(event.rChannel.id, function(cStats) { + if(!event.channel) return callback(); + var id = event.channel + '.' + event.server; + this.api.getChannelStats(id, function(cStats) { if(cStats) { event.cStats = cStats; callback(); } else { - this.api.createChannelStats(event.rChannel.id, function(cStats) { + this.api.createChannelStats(id, function(cStats) { event.cStats = cStats; callback(); }); diff --git a/modules/udp/udp.js b/modules/udp/udp.js index 52caac6..6a4b5a7 100644 --- a/modules/udp/udp.js +++ b/modules/udp/udp.js @@ -14,7 +14,9 @@ var udp = function(dbot) { dbot.say(this.config.server, this.config.channel, message); } }.bind(this)); - server.bind(this.config.port); + this.onLoad = function() { + server.bind(this.config.port); + }.bind(this); }; exports.fetch = function(dbot) { diff --git a/modules/users/api.js b/modules/users/api.js index 00ec82e..b9368bc 100644 --- a/modules/users/api.js +++ b/modules/users/api.js @@ -1,168 +1,73 @@ -var _ = require('underscore')._, - uuid = require('node-uuid'), - databank = require('databank'); +var _ = require('underscore')._; var api = function(dbot) { - var escapeRegexen = function(str) { - return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); - }; - var api = { - // Return a user record given a primary nick or an alias - 'resolveUser': function(server, nick, callback, lc) { - var user = false; - if(lc) nick = nick.toLowerCase(); - if(_.has(this.userCache[server], nick)) { - this.api.getUser(this.userCache[server][nick], callback); - } else { - this.db.search('users', { 'server': server }, function(result) { - if(lc) { - result.primaryNick = result.primaryNick.toLowerCase(); - _.each(result.aliases, function(v, k) { - result.aliases[k] = v.toLowerCase(); - }); - } - - if(result.primaryNick == nick || _.include(result.aliases, nick)) { - user = result; - } - }.bind(this), function(err) { - if(user) { - if(!_.has(this.userCache[server], nick)) { - this.userCache[server][nick] = user.id; - } else if(this.userCache[server][nick] !== user.id) { - this.userCache[server][nick] = user.id; + // Retrieve a user record given a server and nickname + 'resolveUser': function(server, nick, callback) { + var id = nick + '.' + server; + this.api.getUser(id, function(err, result) { + if(!err) { + callback(null, result); + } else { + this.db.read('user_aliases', id, function(err, result) { + if(!err) { + this.api.getUser(result.user, callback); + } else { + callback(true, null); } - } - callback(user); - }.bind(this)); - } - }, - - // Return many user records given primary nicks of aliases - 'resolveUsers': function(server, nicks, callback) { - var users = []; - this.db.search('users', { 'server': server }, function(result) { - var pNicks = result.aliases.slice(0).unshift(result.primaryNick); - for(var i=0;i