def kick(bot, trigger): """ Kick a user from the channel. """ if bot.privileges[trigger.sender][trigger.nick] < OP: return if bot.privileges[trigger.sender][bot.nick] < HALFOP: return bot.reply("I'm not a channel operator!") text = trigger.group().split() argc = len(text) if argc < 2: return opt = Identifier(text[1]) nick = opt channel = trigger.sender reasonidx = 2 if not opt.is_nick(): if argc < 3: return nick = text[2] channel = opt reasonidx = 3 reason = ' '.join(text[reasonidx:]) if nick != bot.config.nick: bot.write(['KICK', channel, nick, reason])
def collectlines(bot, trigger): """Create a temporary log of what people say""" # Don't log things in PM if trigger.is_privmsg: return # Add a log for the channel and nick, if there isn't already one if trigger.sender not in bot.memory['find_lines']: bot.memory['find_lines'][trigger.sender] = WillieMemory() if Identifier( trigger.nick) not in bot.memory['find_lines'][trigger.sender]: bot.memory['find_lines'][trigger.sender][Identifier( trigger.nick)] = list() # Create a temporary list of the user's lines in a channel templist = bot.memory['find_lines'][trigger.sender][Identifier( trigger.nick)] line = trigger.group() if line.startswith("s/"): # Don't remember substitutions return elif line.startswith("\x01ACTION"): # For /me messages line = line[:-1] templist.append(line) else: templist.append(line) del templist[:-10] # Keep the log to 10 lines per person bot.memory['find_lines'][trigger.sender][Identifier( trigger.nick)] = templist
def ban(bot, trigger): """ This give admins the ability to ban a user. The bot must be a Channel Operator for this command to work. """ if bot.privileges[trigger.sender][trigger.nick] < OP: return if bot.privileges[trigger.sender][bot.nick] < HALFOP: return bot.reply("I'm not a channel operator!") text = trigger.group().split() argc = len(text) if argc < 2: return opt = Identifier(text[1]) banmask = opt channel = trigger.sender if not opt.is_nick(): if argc < 3: return channel = opt banmask = text[2] banmask = configureHostMask(banmask) if banmask == '': return bot.write(['MODE', channel, '+b', banmask])
def receive_info(bot, trigger): if trigger.sender != 'NickServ': return account = Identifier(trigger.group(2)) nick = Identifier(trigger.group(1)) try: bot.db.alias_nick(account, nick) except ValueError as e: try: bot.db.alias_nick(nick, account) except ValueError as e: if nick in force: bot.db.merge_nick_groups(account, nick) first_id = bot.db.get_nick_id(Identifier(account)) second_id = bot.db.get_nick_id(Identifier(nick)) bot.db.execute('UPDATE nicknames SET nick_id = ? WHERE nick_id = ?', [first_id, second_id]) bot.msg(nick, 'Merged {0} and {1}. If conflicting values were found' \ ' between accounts, values from {0} were used.'.format(account, nick)) del force[nick] else: extra = '' if nick.lower() != account.lower(): extra = 'If you wish to merge data' \ ' from {0} to {1}, you may do so by using `.alias merge`. Please note that doing so' \ ' will overwrite conflicting values with those found in {0}. '.format(account, nick) bot.msg(nick, 'Sorry, I was unable to alias your nick' \ ' to your account -- it might have already been aliased. {1}({0})'.format(e.message, extra)) return bot.msg(nick, 'Successfully aliased ' + nick + ' to account ' + account)
def kickban(bot, trigger): """ This gives admins the ability to kickban a user. The bot must be a Channel Operator for this command to work. .kickban [#chan] user1 user!*@* get out of here """ if bot.privileges[trigger.sender][bot.nick] < HALFOP: return bot.reply("I'm not a channel operator!") text = trigger.group().split() argc = len(text) if argc < 4: return opt = Identifier(text[1]) nick = opt mask = text[2] reasonidx = 3 if not opt.is_nick(): if argc < 5: return channel = opt nick = text[2] mask = text[3] reasonidx = 4 reason = ' '.join(text[reasonidx:]) mask = configureHostMask(mask) if mask == '': return bot.write(['MODE', channel, '+b', mask]) bot.write(['KICK', channel, nick, ' :', reason])
def unquiet(bot, trigger): """ This gives admins the ability to unquiet a user. The bot must be a Channel Operator for this command to work. """ if bot.privileges[trigger.sender][trigger.nick] < OP: return if bot.privileges[trigger.sender][bot.nick] < OP: return bot.reply("I'm not a channel operator!") text = trigger.group().split() argc = len(text) if argc < 2: return opt = Identifier(text[1]) quietmask = opt channel = trigger.sender if not opt.is_nick(): if argc < 3: return quietmask = text[2] channel = opt quietmask = configureHostMask(quietmask) if quietmask == '': return bot.write(['MODE', opt, '-q', quietmask])
def seen(bot, trigger): """Reports when and where the user was last seen.""" if not trigger.group(2): bot.say(".seen <nick> - Reports when <nick> was last seen.") return nick = Identifier(trigger.group(2).strip()) if nick in seen_dict: timestamp = seen_dict[nick]['timestamp'] channel = seen_dict[nick]['channel'] message = seen_dict[nick]['message'] tz = get_timezone(bot.db, bot.config, None, trigger.nick, trigger.sender) saw = datetime.datetime.utcfromtimestamp(timestamp) timestamp = format_time(bot.db, bot.config, tz, trigger.nick, trigger.sender, saw) msg = "I last saw {} at {}".format(nick, timestamp) if Identifier(channel) == trigger.sender: msg = msg + " in here, saying " + message else: msg += " in another channel." bot.say(str(trigger.nick) + ': ' + msg) else: bot.say("Sorry, I haven't seen %s around." % nick)
def test_get_nick_id(db): conn = sqlite3.connect(db_filename) tests = [ [None, 'embolalia', Identifier('Embolalia')], # Ensures case conversion is handled properly [None, '[][]', Identifier('[]{}')], # Unicode, just in case [None, 'embölaliå', Identifier('EmbölaliÅ')], ] for test in tests: test[0] = db.get_nick_id(test[2]) nick_id, slug, nick = test with conn: cursor = conn.cursor() registered = cursor.execute( 'SELECT nick_id, slug, canonical FROM nicknames WHERE canonical IS ?', [nick]).fetchall() assert len(registered) == 1 assert registered[0][1] == slug and registered[0][2] == nick # Check that each nick ended up with a different id assert len(set(test[0] for test in tests)) == len(tests) # Check that the retrieval actually is idempotent for test in tests: nick_id = test[0] new_id = db.get_nick_id(test[2]) assert nick_id == new_id # Even if the case is different for test in tests: nick_id = test[0] new_id = db.get_nick_id(Identifier(test[2].upper())) assert nick_id == new_id
def bomb_glue(bot, trigger): old = trigger.nick new = Identifier(trigger) with lock: if old.lower() in BOMBS: BOMBS[new.lower()] = BOMBS.pop(old.lower()) bot.notice("There's still a bomb in your pants, %s!" % new, new)
def get_nick_or_channel_value(self, name, key): """Gets the value `key` associated to the nick or channel `name`. """ name = Identifier(name) if name.is_nick(): return self.get_nick_value(name, key) else: return self.get_channel_value(name, key)
def get_nick_value(self, nick, key): """Retrieves the value for a given key associated with a nick.""" nick = Identifier(nick) result = self.execute( 'SELECT value FROM nicknames, nick_values WHERE slug = ? AND key = ?', [nick.lower(), key]).fetchone() if result is not None: result = result[0] return _deserialize(result)
def get_count(bot, user, chan): chan_count = bot.memory['pls_count'] # only do something if there is conversation to work with if chan not in chan_count: return 0 if Identifier(user) not in chan_count[chan]: return 0 return chan_count[chan][Identifier(user)]
def start(bot, trigger): """ Put a bomb in the specified user's pants. They will be kicked if they don't guess the right wire fast enough. """ if not trigger.group(3): bot.say("Who do you want to bomb?") return NOLIMIT if bot.db.get_channel_value(trigger.sender, 'bombs_disabled'): bot.notice("An admin has disabled bombing in %s." % trigger.sender, trigger.nick) return NOLIMIT since_last = time_since_bomb(bot, trigger.nick) if since_last < TIMEOUT and not trigger.admin: bot.notice("You must wait %.0f seconds before you can bomb someone again." % (TIMEOUT - since_last), trigger.nick) return global BOMBS target = Identifier(trigger.group(3)) target_unbombable = bot.db.get_nick_value(target, 'unbombable') if target == bot.nick: bot.say("You thought you could trick me into bombing myself?!") return NOLIMIT if target == trigger.nick: bot.say("%s pls. Bomb a friend if you have to!" % trigger.nick) return NOLIMIT if target.lower() not in bot.privileges[trigger.sender.lower()]: bot.say("You can't bomb imaginary people!") return NOLIMIT if target_unbombable and not trigger.admin: bot.say("I'm not allowed to bomb %s, sorry." % target) return NOLIMIT if bot.db.get_nick_value(trigger.nick, 'unbombable'): bot.say("Try again when you're bombable yourself, %s." % trigger.nick) return NOLIMIT with lock: if target.lower() in BOMBS: bot.say("I can't fit another bomb in %s's pants!" % target) return NOLIMIT wires = [COLORS[i] for i in sorted(sample(xrange(len(COLORS)), randrange(3, 5)))] num_wires = len(wires) wires_list = [formatting.color(str(wire), str(wire)) for wire in wires] wires_list = ", ".join(wires_list[:-2] + [" and ".join(wires_list[-2:])]).replace('Light_', '') wires = [wire.replace('Light_', '') for wire in wires] color = choice(wires) bot.say("Hey, %s! I think there's a bomb in your pants. %s timer, %d wires: %s. " "Which wire would you like to cut? (respond with %scutwire color)" % (target, FUSE_TEXT, num_wires, wires_list, bot.config.core.help_prefix or '.')) bot.notice("Hey, don't tell %s, but it's the %s wire." % (target, color), trigger.nick) if target_unbombable: bot.notice("Just so you know, %s is marked as unbombable." % target, trigger.nick) timer = Timer(FUSE, explode, (bot, trigger)) BOMBS[target.lower()] = (wires, color, timer, target) timer.start() bombs_planted = bot.db.get_nick_value(trigger.nick, 'bombs_planted') or 0 bot.db.set_nick_value(trigger.nick, 'bombs_planted', bombs_planted + 1) bot.db.set_nick_value(trigger.nick, 'bomb_last_planted', time.time())
def get_nick_value(self, nick, key): """Retrieves the value for a given key associated with a nick.""" nick = Identifier(nick) result = self.execute( 'SELECT value FROM nicknames, nick_values WHERE slug = ? AND key = ?', [nick.lower(), key] ).fetchone() if result is not None: result = result[0] return _deserialize(result)
def unalias_nick(self, alias): """Removes an alias. Raises ValueError if there is not at least one other nick in the group. To delete an entire group, use `delete_group`. """ alias = Identifier(alias) nick_id = self.get_nick_id(alias, False) count = self.execute("SELECT COUNT(*) FROM nicknames WHERE nick_id = ?", [nick_id]).fetchone()[0] if count == 0: raise ValueError("Given alias is the only entry in its group.") self.execute("DELETE FROM nicknames WHERE slug = ?", [alias.lower()])
def unalias_nick(self, alias): """Removes an alias. Raises ValueError if there is not at least one other nick in the group. To delete an entire group, use `delete_group`. """ alias = Identifier(alias) nick_id = self.get_nick_id(alias, False) count = self.execute('SELECT COUNT(*) FROM nicknames WHERE nick_id = ?', [nick_id]).fetchone()[0] if count == 0: raise ValueError('Given alias is the only entry in its group.') self.execute('DELETE FROM nicknames WHERE slug = ?', [alias.lower()])
def f_remind(bot, trigger): """Give someone a message the next time they're seen""" teller = trigger.nick verb = trigger.group(1) if not trigger.group(3): bot.reply("%s whom?" % verb) return tellee = trigger.group(3).rstrip('.,:;') msg = trigger.group(2).lstrip(tellee).lstrip() if not msg: bot.reply("%s %s what?" % (verb, tellee)) return tellee = Identifier(tellee) if not os.path.exists(bot.tell_filename): return if len(tellee) > 20: return bot.reply('That nickname is too long.') if tellee == bot.nick: return bot.reply("I'm here now, you can tell me whatever you want!") if not tellee in (Identifier(teller), bot.nick, 'me'): tz = willie.tools.get_timezone(bot.db, bot.config, None, tellee) timenow = willie.tools.format_time(bot.db, bot.config, tz, tellee) bot.memory['tell_lock'].acquire() try: if not tellee in bot.memory['reminders']: bot.memory['reminders'][tellee] = [(teller, verb, timenow, msg) ] else: bot.memory['reminders'][tellee].append( (teller, verb, timenow, msg)) finally: bot.memory['tell_lock'].release() response = "I'll pass that on when %s is around." % tellee bot.reply(response) elif Identifier(teller) == tellee: bot.say('You can %s yourself that.' % verb) else: bot.say("Hey, I'm not as stupid as Monty you know!") dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
def alias_nick(self, nick, alias): """Create an alias for a nick. Raises ValueError if the alias already exists. If nick does not already exist, it will be added along with the alias.""" nick = Identifier(nick) alias = Identifier(alias) nick_id = self.get_nick_id(nick) sql = "INSERT INTO nicknames (nick_id, slug, canonical) VALUES (?, ?, ?)" values = [nick_id, alias.lower(), alias] try: self.execute(sql, values) except sqlite3.IntegrityError as e: raise ValueError("Alias already exists.")
def receive_info(bot, trigger): if trigger.sender != 'NickServ': return account = Identifier(trigger.group(2)) nick = Identifier(trigger.group(1)) try: bot.db.alias_nick(account, nick) except ValueError as e: try: bot.db.alias_nick(nick, account) except ValueError as e: if nick in force: bot.db.merge_nick_groups(account, nick) first_id = bot.db.get_nick_id(Identifier(account)) second_id = bot.db.get_nick_id(Identifier(nick)) bot.db.execute( 'UPDATE nicknames SET nick_id = ? WHERE nick_id = ?', [first_id, second_id]) bot.msg(nick, 'Merged {0} and {1}. If conflicting values were found' \ ' between accounts, values from {0} were used.'.format(account, nick)) del force[nick] else: extra = '' if nick.lower() != account.lower(): extra = 'If you wish to merge data' \ ' from {0} to {1}, you may do so by using `.alias merge`. Please note that doing so' \ ' will overwrite conflicting values with those found in {0}. '.format(account, nick) bot.msg(nick, 'Sorry, I was unable to alias your nick' \ ' to your account -- it might have already been aliased. {1}({0})'.format(e.message, extra)) return bot.msg(nick, 'Successfully aliased ' + nick + ' to account ' + account)
def alias_nick(self, nick, alias): """Create an alias for a nick. Raises ValueError if the alias already exists. If nick does not already exist, it will be added along with the alias.""" nick = Identifier(nick) alias = Identifier(alias) nick_id = self.get_nick_id(nick) sql = 'INSERT INTO nicknames (nick_id, slug, canonical) VALUES (?, ?, ?)' values = [nick_id, alias.lower(), alias] try: self.execute(sql, values) except sqlite3.IntegrityError as e: raise ValueError('Alias already exists.')
def watch(bot, trigger): """Receive a notification when a user speaks. Use * on the end of [nick] to match multiple nicks (e.g Tell*)""" if trigger.is_privmsg is False: return bot.reply("This command only works in PMs.") teller = trigger.nick verb = trigger.group(1) if not trigger.group(3): bot.reply("%s whom?" % verb) return tellee = trigger.group(3).rstrip('.,:;') tellee = Identifier(tellee) if len(tellee) > 20: return bot.reply('That nickname is too long.') if tellee == bot.nick or tellee == 'Cashy': return bot.reply("[-_-]") if not tellee in (Identifier(teller), bot.nick, 'me'): timenow = time.time() bot.memory['tell_lock'].acquire() try: if not tellee in bot.memory['tell_dict']: bot.memory['tell_dict'][tellee] = [(teller, verb, timenow, '')] else: found = False for (_teller, _verb, _datetime, _msg) in bot.memory['tell_dict'][tellee]: if verb.lower()=='watch': if _teller == teller: found = True break if found: return bot.say("You already have me watching for %s." % tellee) else: bot.memory['tell_dict'][tellee].append((teller, verb, timenow, '')) finally: bot.memory['tell_lock'].release() bot.reply("I'll let you know when I see %s." % tellee) elif Identifier(teller) == tellee: bot.say('[-_-]') else: bot.say("[-_-]") storage.put('tell',bot.memory['tell_dict'])
def seen(bot, trigger): """Reports when and where the user was last seen.""" if not trigger.group(2): bot.say(".seen <nick> - Reports when <nick> was last seen.") return nick = trigger.group(2).strip() timestamp = bot.db.get_nick_value(nick, 'seen_timestamp') if timestamp: channel = bot.db.get_nick_value(nick, 'seen_channel') message = bot.db.get_nick_value(nick, 'seen_message') tz = get_timezone(bot.db, bot.config, None, trigger.nick, trigger.sender) saw = datetime.datetime.utcfromtimestamp(timestamp) timestamp = format_time(bot.db, bot.config, tz, trigger.nick, trigger.sender, saw) msg = "I last saw {} at {}".format(nick, timestamp) if Identifier(channel) == trigger.sender: msg = msg + " in here, saying " + message else: msg += " in another channel." bot.say(str(trigger.nick) + ': ' + msg) else: bot.say("Sorry, I haven't seen {} around.".format(nick))
def check_alias(bot, trigger): if not trigger.group(3): bot.reply('alias usage: .alias <add|merge|list>') return if (trigger.group(3).lower() == 'add'): bot.write(['PRIVMSG', 'NickServ', ':info', trigger.nick]) bot.reply('Fetching NickServ info... I will get back to you in a PM') elif (trigger.group(3).lower() == 'merge'): force[trigger.nick] = True bot.write(['PRIVMSG', 'NickServ', ':info', trigger.nick]) bot.reply('Fetching NickServ info... I will get back to you in a PM') elif (trigger.group(3).lower() == 'list'): try: alias = Identifier(trigger.nick) nick_id = bot.db.get_nick_id(alias, False) nicks = bot.db.execute( 'SELECT DISTINCT canonical FROM nicknames WHERE nick_id = ?', [nick_id]).fetchall() bot.say('{}, your aliases are: {}'.format( trigger.nick, ' '.join([nick[0] for nick in nicks]))) except: bot.say( 'Something went wrong, perhaps you haven\'t aliased any nicks?' )
def explode(bot, trigger): target = Identifier(trigger.group(3)) orig_target = target with lock: if target.lower() not in BOMBS: # nick change happened for nick in BOMBS.keys(): if BOMBS[nick][3] == target: target = Identifier(nick) break bot.say("%s pls, you could've at least picked one! Now you're dead. You see that? " "Guts, all over the place. (You should've picked the %s wire.)" % (target, BOMBS[target.lower()][1])) kickboom(bot, trigger, target) BOMBS.pop(target.lower()) timeouts = bot.db.get_nick_value(orig_target, 'bomb_timeouts') or 0 bot.db.set_nick_value(orig_target, 'bomb_timeouts', timeouts + 1)
def test_merge_nick_groups(db): conn = sqlite3.connect(db_filename) aliases = ['Embolalia', 'Embo'] for nick_id, alias in enumerate(aliases): conn.execute('INSERT INTO nicknames VALUES (?, ?, ?)', [nick_id, Identifier(alias).lower(), alias]) conn.commit() finals = (('foo', 'bar'), ('bar', 'blue'), ('spam', 'eggs')) db.set_nick_value(aliases[0], finals[0][0], finals[0][1]) db.set_nick_value(aliases[0], finals[1][0], finals[1][1]) db.set_nick_value(aliases[1], 'foo', 'baz') db.set_nick_value(aliases[1], finals[2][0], finals[2][1]) db.merge_nick_groups(aliases[0], aliases[1]) nick_id = conn.execute('SELECT nick_id FROM nicknames').fetchone()[0] alias_id = conn.execute('SELECT nick_id FROM nicknames').fetchone()[0] assert nick_id == alias_id for key, value in finals: found = conn.execute( 'SELECT value FROM nick_values WHERE nick_id = ? AND key = ?', [nick_id, key]).fetchone()[0] assert json.loads(unicode(found)) == value
def delete_nick_group(self, nick): """Removes a nickname, and all associated aliases and settings. """ nick = Identifier(nick) nick_id = self.get_nick_id(nick, False) self.execute('DELETE FROM nicknames WHERE nick_id = ?', [nick_id]) self.execute('DELETE FROM nick_values WHERE nick_id = ?', [nick_id])
def set_nick_value(self, nick, key, value): """Sets the value for a given key to be associated with the nick.""" nick = Identifier(nick) value = json.dumps(value, ensure_ascii=False) nick_id = self.get_nick_id(nick) self.execute('INSERT OR REPLACE INTO nick_values VALUES (?, ?, ?)', [nick_id, key, value])
def format_count(bot, trigger): if trigger.is_privmsg: return user = Identifier(trigger.group(2) or trigger.nick) user = Identifier(user.strip()) count = get_count(bot, user, trigger.sender) since = bot.memory['pls_count_time'] timezone = get_timezone(bot.db, bot.config, None, trigger.nick) if not timezone: timezone = 'UTC' time = format_time(bot.db, bot.config, timezone, trigger.nick, trigger.sender, datetime.datetime.fromtimestamp(since)) bot.say('{} has said pls in {} {} time(s) since {}'.format(user, trigger.sender, count, time))
def tell(bot, trigger): """Give someone a message the next time they're seen. Use * on the end of [recipient] to match multiple nicks (e.g Tell*)""" if trigger.is_privmsg is False: return bot.reply("This command only works in PMs.") teller = trigger.nick verb = trigger.group(1) if not trigger.group(3): bot.reply("%s whom?" % verb) return tellee = trigger.group(3).rstrip('.,:;') msg = trigger.group(2).lstrip(tellee).lstrip() if not msg: bot.reply("%s %s what?" % (verb, tellee)) return tellee = Identifier(tellee) if len(tellee) > 20: return bot.reply('That nickname is too long.') if tellee == bot.nick or tellee == 'Cashy': return bot.reply("I'm right here.") if not tellee in (Identifier(teller), bot.nick, 'me'): timenow = time.time() bot.memory['tell_lock'].acquire() try: if not tellee in bot.memory['tell_dict']: bot.memory['tell_dict'][tellee] = [(teller, verb, timenow, msg)] else: bot.memory['tell_dict'][tellee].append((teller, verb, timenow, msg)) finally: bot.memory['tell_lock'].release() bot.reply("I'll pass that on when %s is around." % tellee) elif Identifier(teller) == tellee: bot.say('You can %s yourself that.' % verb) else: bot.say("[-_-]") storage.put('tell',bot.memory['tell_dict'])
def get_channel_value(self, channel, key): """Retrieves the value for a given key associated with a channel.""" channel = Identifier(channel).lower() result = self.execute( 'SELECT value FROM channel_values WHERE channel = ? AND key = ?', [channel, key]).fetchone() if result is not None: result = result[0] return _deserialize(result)
def promote_karma(bot, trigger): """ Update karma status for specify IRC user if get '++' message. """ if (trigger.is_privmsg): return bot.say('People like it when you tell them good things.') if (bot.db.get_nick_id(Identifier(trigger.group(1))) == bot.db.get_nick_id( Identifier(trigger.nick))): return bot.say('You may not give yourself karma!') current_karma = bot.db.get_nick_value(trigger.group(1), 'karma') if not current_karma: current_karma = 0 else: current_karma = int(current_karma) current_karma += 1 bot.db.set_nick_value(trigger.group(1), 'karma', current_karma) bot.say(trigger.group(1) + ' == ' + str(current_karma))
def demote_karma(bot, trigger): """ Update karma status for specify IRC user if get '--' message. """ if (trigger.is_privmsg): return bot.say('Say it to their face!') if (bot.db.get_nick_id(Identifier(trigger.group(1))) == bot.db.get_nick_id( Identifier(trigger.nick))): return bot.say('You may not reduce your own karma!') current_karma = bot.db.get_nick_value(trigger.group(1), 'karma') if not current_karma: current_karma = 0 else: current_karma = int(current_karma) current_karma -= 1 bot.db.set_nick_value(trigger.group(1), 'karma', current_karma) bot.say(trigger.group(1) + ' == ' + str(current_karma))
def track_modes(bot, trigger): """Track usermode changes and keep our lists of ops up to date.""" # Mode message format: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> ) channel = Identifier(trigger.args[0]) line = trigger.args[1:] # If the first character of where the mode is being set isn't a # # then it's a user mode, not a channel mode, so we'll ignore it. if channel.is_nick(): return mapping = { 'v': willie.module.VOICE, 'h': willie.module.HALFOP, 'o': willie.module.OP, 'a': willie.module.ADMIN, 'q': willie.module.OWNER } modes = [] for arg in line: if len(arg) == 0: continue if arg[0] in '+-': # There was a comment claiming IRC allows e.g. MODE +aB-c foo, but # I don't see it in any RFCs. Leaving in the extra parsing for now. sign = '' modes = [] for char in arg: if char == '+' or char == '-': sign = char else: modes.append(sign + char) else: arg = Identifier(arg) for mode in modes: priv = bot.privileges[channel].get(arg, 0) value = mapping.get(mode[1]) if value is not None: if mode[0] == '+': priv = priv | value else: priv = priv & ~value bot.privileges[channel][arg] = priv
def track_modes(bot, trigger): """Track usermode changes and keep our lists of ops up to date.""" # Mode message format: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> ) channel = Identifier(trigger.args[0]) line = trigger.args[1:] # If the first character of where the mode is being set isn't a # # then it's a user mode, not a channel mode, so we'll ignore it. if channel.is_nick(): return mapping = { "v": willie.module.VOICE, "h": willie.module.HALFOP, "o": willie.module.OP, "a": willie.module.ADMIN, "q": willie.module.OWNER, } modes = [] for arg in line: if len(arg) == 0: continue if arg[0] in "+-": # There was a comment claiming IRC allows e.g. MODE +aB-c foo, but # I don't see it in any RFCs. Leaving in the extra parsing for now. sign = "" modes = [] for char in arg: if char == "+" or char == "-": sign = char else: modes.append(sign + char) else: arg = Identifier(arg) for mode in modes: priv = bot.privileges[channel].get(arg, 0) value = mapping.get(mode[1]) if value is not None: if mode[0] == "+": priv = priv | value else: priv = priv & ~value bot.privileges[channel][arg] = priv
def test_unalias_nick(db): conn = sqlite3.connect(db_filename) nick = 'Embolalia' nick_id = 42 conn.execute('INSERT INTO nicknames VALUES (?, ?, ?)', [nick_id, Identifier(nick).lower(), nick]) aliases = ['EmbölaliÅ', 'Embo`work', 'Embo'] for alias in aliases: conn.execute('INSERT INTO nicknames VALUES (?, ?, ?)', [nick_id, Identifier(alias).lower(), alias]) conn.commit() for alias in aliases: db.unalias_nick(alias) for alias in aliases: found = conn.execute('SELECT * FROM nicknames WHERE nick_id = ?', [nick_id]).fetchall() assert len(found) == 1
def format_count(bot, trigger): if trigger.is_privmsg: return user = Identifier(trigger.group(2) or trigger.nick) user = Identifier(user.strip()) count = get_count(bot, user, trigger.sender) since = bot.memory['pls_count_time'] timezone = get_timezone(bot.db, bot.config, None, trigger.nick) if not timezone: timezone = 'UTC' time = format_time(bot.db, bot.config, timezone, trigger.nick, trigger.sender, datetime.datetime.fromtimestamp(since)) bot.say('{} has said pls in {} {} time(s) since {}'.format( user, trigger.sender, count, time))
def _nick_blocked(self, nick): bad_nicks = self.config.core.get_list('nick_blocks') for bad_nick in bad_nicks: bad_nick = bad_nick.strip() if not bad_nick: continue if (re.match(bad_nick + '$', nick, re.IGNORECASE) or Identifier(bad_nick) == nick): return True return False
def merge_nick_groups(self, first_nick, second_nick): """Merges the nick groups for the specified nicks. Takes two nicks, which may or may not be registered. Unregistered nicks will be registered. Keys which are set for only one of the given nicks will be preserved. Where multiple nicks have values for a given key, the value set for the first nick will be used. Note that merging of data only applies to the native key-value store. If modules define their own tables which rely on the nick table, they will need to have their merging done separately.""" first_id = self.get_nick_id(Identifier(first_nick)) second_id = self.get_nick_id(Identifier(second_nick)) self.execute( 'UPDATE OR IGNORE nick_values SET nick_id = ? WHERE nick_id = ?', [first_id, second_id]) self.execute('DELETE FROM nick_values WHERE nick_id = ?', [second_id]) self.execute('UPDATE nicknames SET nick_id = ? WHERE nick_id = ?', [first_id, second_id])
def track_nicks(bot, trigger): """Track nickname changes and maintain our chanops list accordingly.""" old = trigger.nick new = Identifier(trigger) # Give debug mssage, and PM the owner, if the bot's own nick changes. if old == bot.nick: privmsg = ("Hi, I'm your bot, %s." "Something has made my nick change. " "This can cause some problems for me, " "and make me do weird things. " "You'll probably want to restart me, " "and figure out what made that happen " "so you can stop it happening again. " "(Usually, it means you tried to give me a nick " "that's protected by NickServ.)") % bot.nick debug_msg = ( "Nick changed by server. " "This can cause unexpected behavior. Please restart the bot.") LOGGER.critical(debug_msg) bot.msg(bot.config.core.owner, privmsg) return for channel in bot.privileges: channel = Identifier(channel) if old in bot.privileges[channel]: value = bot.privileges[channel].pop(old) bot.privileges[channel][new] = value # Old privilege maintenance for channel in bot.halfplus: if old in bot.halfplus[channel]: bot.del_halfop(channel, old) bot.add_halfop(channel, new) for channel in bot.ops: if old in bot.ops[channel]: bot.del_op(channel, old) bot.add_op(channel, new) for channel in bot.voices: if old in bot.voices[channel]: bot.del_voice(channel, old) bot.add_voice(channel, new)
def cutwire(bot, trigger): """ Tells willie to cut a wire when you've been bombed. """ global BOMBS target = Identifier(trigger.nick) with lock: if target.lower() != bot.nick.lower() and target.lower() not in BOMBS: bot.say("You can't cut a wire until someone bombs you, %s." % target) return if not trigger.group(3): bot.say("You have to choose a wire to cut.") return # Remove target from bomb list temporarily wires, color, timer, orig_target = BOMBS.pop(target.lower()) wirecut = trigger.group(3) if wirecut.lower() in ('all', 'all!'): timer.cancel() # defuse timer, execute premature detonation bot.say("Cutting ALL the wires! (You should've picked the %s wire.)" % color) kickboom(bot, trigger, target) alls = bot.db.get_nick_value(orig_target, 'bomb_alls') or 0 bot.db.set_nick_value(orig_target, 'bomb_alls', alls + 1) elif wirecut.capitalize() not in wires: bot.say("That wire isn't here, %s! You sure you're picking the right one?" % target) # Add the target back onto the bomb list BOMBS[target.lower()] = (wires, color, timer, orig_target) elif wirecut.capitalize() == color: bot.say("You did it, %s! I'll be honest, I thought you were dead. " "But nope, you did it. You picked the right one. Well done." % target) timer.cancel() # defuse bomb defuses = bot.db.get_nick_value(orig_target, 'bomb_defuses') or 0 bot.db.set_nick_value(orig_target, 'bomb_defuses', defuses + 1) else: timer.cancel() # defuse timer, execute premature detonation bot.say("Nope, wrong wire! Aww, now you've gone and killed yourself. " "Wow. Sorry. (You should've picked the %s wire.)" % color) kickboom(bot, trigger, target) wrongs = bot.db.get_nick_value(orig_target, 'bomb_wrongs') or 0 bot.db.set_nick_value(orig_target, 'bomb_wrongs', wrongs + 1)
def track_modes(bot, trigger): """Track usermode changes and keep our lists of ops up to date.""" # Mode message format: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> ) channel = Identifier(trigger.args[0]) line = trigger.args[1:] # If the first character of where the mode is being set isn't a # # then it's a user mode, not a channel mode, so we'll ignore it. if channel.is_nick(): return def handle_old_modes(nick, mode): #Old mode maintenance. Drop this crap in 5.0. if mode[1] == 'o' or mode[1] == 'q' or mode[1] == 'a': if mode[0] == '+': bot.add_op(channel, nick) else: bot.del_op(channel, nick) elif mode[1] == 'h': # Halfop if mode[0] == '+': bot.add_halfop(channel, nick) else: bot.del_halfop(channel, nick) elif mode[1] == 'v': if mode[0] == '+': bot.add_voice(channel, nick) else: bot.del_voice(channel, nick) mapping = {'v': willie.module.VOICE, 'h': willie.module.HALFOP, 'o': willie.module.OP, 'a': willie.module.ADMIN, 'q': willie.module.OWNER} modes = [] for arg in line: if len(arg) == 0: continue if arg[0] in '+-': # There was a comment claiming IRC allows e.g. MODE +aB-c foo, but # I don't see it in any RFCs. Leaving in the extra parsing for now. sign = '' modes = [] for char in arg: if char == '+' or char == '-': sign = char else: modes.append(sign + char) else: arg = Identifier(arg) for mode in modes: priv = bot.privileges[channel].get(arg, 0) value = mapping.get(mode[1]) if value is not None: if mode[0] == '+': priv = priv | value else: priv = priv & ~value bot.privileges[channel][arg] = priv handle_old_modes(arg, mode)