def alias_nick(self, nick, alias): """Create an alias for a nick. :param str nick: an existing nickname :param str alias: an alias by which ``nick`` should also be known :raise ValueError: if the ``alias`` already exists :raise ~sqlalchemy.exc.SQLAlchemyError: if there is a database error .. seealso:: To merge two *existing* nick groups, use :meth:`merge_nick_groups`. To remove an alias created with this function, use :meth:`unalias_nick`. """ nick = Identifier(nick) alias = Identifier(alias) nick_id = self.get_nick_id(nick) session = self.ssession() try: result = session.query(Nicknames) \ .filter(Nicknames.slug == alias.lower()) \ .filter(Nicknames.canonical == alias) \ .one_or_none() if result: raise ValueError('Alias already exists.') nickname = Nicknames(nick_id=nick_id, slug=alias.lower(), canonical=alias) session.add(nickname) session.commit() except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
def cancel_bomb(bot, trigger): """ Lets a bomber disarm the bomb they set on the specified user. Does not reset the cooldown timer. (Bot admins can cancel bombs on any player in the channel.) """ target = trigger.group(3) or None if not target: for bomb in BOMBS: if trigger.nick == BOMBS[bomb]['bomber']: target = BOMBS[bomb]['target'] break if not target: return bot.reply(STRINGS['CANCEL_WHOM']) target = Identifier(target) # issue #24 with lock: if target.lower() not in BOMBS: bot.reply(STRINGS['CANCEL_NO_BOMB'] % target) return if trigger.nick != BOMBS[target.lower()]['bomber'] and not trigger.admin: bot.reply(STRINGS['CANCEL_NO_PERMISSION'] % target) return bomber = BOMBS[target.lower()]['bomber'] bombs_planted = bot.db.get_nick_value(bomber, 'bombs_planted') or 0 bot.db.set_nick_value(bomber, 'bombs_planted', bombs_planted - 1) BOMBS.pop(target.lower())['timer'].cancel() bot.say(STRINGS['CANCEL_DONE'] % target)
def cutwire(bot, trigger): """ Tells sopel to cut a wire when you've been bombed. """ global bombs, colors target = Identifier(trigger.nick) if target.lower() != bot.nick.lower() and target.lower() not in bombs: bot.say('You can\'t cut a wire till someone bombs you') return if not trigger.group(2): bot.say('You have to choose a wire to cut.') return color, code = bombs.pop(target.lower()) # remove target from bomb list wirecut = trigger.group(2).rstrip(' ') if wirecut.lower() in ('all', 'all!'): sch.cancel(code) # defuse timer, execute premature detonation kmsg = ('KICK %s %s : Cutting ALL the wires! *boom* (You should\'ve picked the %s wire.)' % (trigger.sender, target, color)) bot.write([kmsg]) elif wirecut.capitalize() not in colors: bot.say('I can\'t seem to find that wire, ' + target + '! You sure you\'re picking the right one? It\'s not here!') bombs[target.lower()] = (color, code) # Add the target back onto the bomb list, elif wirecut.capitalize() == color: bot.say('You did it, ' + target + '! I\'ll be honest, I thought you were dead. But nope, you did it. You picked the right one. Well done.') sch.cancel(code) # defuse bomb else: sch.cancel(code) # defuse timer, execute premature detonation kmsg = 'KICK ' + trigger.sender + ' ' + target + \ ' : No! No, that\'s the wrong one. Aww, you\'ve gone and killed yourself. Oh, that\'s... that\'s not good. No good at all, really. Wow. Sorry. (You should\'ve picked the ' + color + ' wire.)' bot.write([kmsg])
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 if not trigger.sender.startswith('#'): bot.say('Tell me this in a channel') return global bombs global sch target = Identifier(trigger.group(3)) if target == bot.nick: bot.say('I will NOT BOMB MYSELF!') return if target.lower() in bombs: bot.say('I can\'t fit another bomb in ' + target + '\'s pants!') return if target == trigger.nick: bot.say('I will not LET YOU BOMB YOURSELF!') return if target.lower() not in bot.privileges[trigger.sender.lower()]: bot.say('Please Bomb someone WHO IS HERE!') return message = 'Hey, ' + target + '! Don\'t look but, I think there\'s a bomb in your pants. 2 minute timer, 5 wires: Red, Yellow, Blue, White and Black. Which wire should I cut? Don\'t worry, I know what I\'m doing! (respond with .cutwire color)' bot.say(message) color = choice(colors) bot.msg(trigger.nick, "Hey, don\'t tell %s, but the %s wire? Yeah, that\'s the one." " But shh! Don\'t say anything!" % (target, color)) code = sch.enter(fuse, 1, explode, (bot, trigger)) bombs[target.lower()] = (color, code) sch.run()
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) session = self.ssession() try: result = session.query(Nicknames) \ .filter(Nicknames.slug == alias.lower()) \ .filter(Nicknames.canonical == alias) \ .one_or_none() if result: raise ValueError('Given alias is the only entry in its group.') nickname = Nicknames(nick_id=nick_id, slug=alias.lower(), canonical=alias) session.add(nickname) session.commit() except SQLAlchemyError: session.rollback() raise finally: session.close()
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]['target'] == target: target = Identifier(nick) break bot.say(STRINGS['NEVER_TRIED'] % (target, BOMBS[target.lower()]['color'])) kickboom(bot, trigger, target, BOMBS[target.lower()]['bomber']) 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 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]['target'] == target: target = Identifier(nick) break bot.say(STRINGS['NEVER_TRIED'] % (target, BOMBS[target.lower()]['color'])) 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 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(STRINGS['BOMB_STILL'] % new, new)
def unalias_nick(self, alias): """Remove an alias. :param str alias: an alias with at least one other nick in its group :raise ValueError: if there is not at least one other nick in the group :raise ~sqlalchemy.exc.SQLAlchemyError: if there is a database error .. seealso:: To delete an entire group, use :meth:`delete_nick_group`. To *add* an alias for a nick, use :meth:`alias_nick`. """ alias = Identifier(alias) nick_id = self.get_nick_id(alias, False) session = self.ssession() try: count = session.query(Nicknames) \ .filter(Nicknames.nick_id == nick_id) \ .count() if count <= 1: raise ValueError('Given alias is the only entry in its group.') session.query(Nicknames).filter( Nicknames.slug == alias.lower()).delete() session.commit() except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
def execute_main(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() message = str(trigger.group(0)) 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 tellee.lower() in [u.lower() for u in bot.users]: if not message.endswith('please'): return bot.reply( "Tell %s that yourself you lazy f**k, they're online now." % tellee) if tellee not in (Identifier(teller), bot.nick, 'me'): tz = get_timezone(bot.db, bot.config, None, tellee) timenow = format_time(bot.db, bot.config, tz, tellee) msg = msg.rstrip('please').rstrip() bot.memory['tell_lock'].acquire() try: if tellee not 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: osd(bot, trigger.sender, 'say', "You can %s yourself that. I'm not your f*****g secretary." % verb) else: osd(bot, trigger.sender, 'say', "Hey, I'm not as stupid as Monty you know!") dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
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 JOIN nick_values ' 'ON nicknames.nick_id = nick_values.nick_id ' 'WHERE slug = ? AND key = ?', [nick.lower(), key]).fetchone() if result is not None: result = result[0] return _deserialize(result)
def verified_nick(bot, nick, channel): nick = re.search('([a-zA-Z0-9\[\]\\`_\^\{\|\}-]{1,32})', nick).group(1) if not nick: return None nick = Identifier(nick) if nick.lower() not in bot.privileges[channel.lower()]: if nick.endswith('--'): if Identifier(nick[:-2]).lower() in bot.privileges[channel.lower()]: return Identifier(nick[:-2]) return None return nick
def cancel_bomb(bot, trigger): """ Cancel the bomb placed on the specified player (can also be used by admins). """ target = trigger.group(3) or None if not target: bot.reply(STRINGS['CANCEL_WHOM']) return target = Identifier(target) # issue #24 with lock: if target.lower() not in BOMBS: bot.reply(STRINGS['CANCEL_NO_BOMB'] % target) return if trigger.nick != BOMBS[target.lower()]['bomber'] and not trigger.admin: bot.reply(STRINGS['CANCEL_NO_PERMISSION'] % target) return bomber = BOMBS[target.lower()]['bomber'] bombs_planted = bot.db.get_nick_value(bomber, 'bombs_planted') or 0 bot.db.set_nick_value(bomber, 'bombs_planted', bombs_planted - 1) BOMBS.pop(target.lower())['timer'].cancel() bot.say(STRINGS['CANCEL_DONE'] % target)
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 JOIN nick_values ' 'ON nicknames.nick_id = nick_values.nick_id ' '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 cutwire(bot, trigger): """ If you've been bombed, tells the bot which wire you want to cut. """ global BOMBS target = Identifier(trigger.nick) if target == bot.nick: # a parallel bot behind a bouncer (e.g. Bucket) can trigger this function (see #16) return with lock: if target.lower() != bot.nick.lower() and target.lower() not in BOMBS: bot.say(STRINGS['CUT_NO_BOMB'] % target) return if not trigger.group(3): bot.say(STRINGS['CUT_NO_WIRE']) return # Remove target from bomb list temporarily bomb = BOMBS.pop(target.lower()) wirecut = trigger.group(3) if wirecut.lower() in ('all', 'all!'): bomb['timer'].cancel() # defuse timer, execute premature detonation bot.say(STRINGS['CUT_ALL_WIRES'] % bomb['color']) kickboom(bot, trigger, target, bomb['bomber']) alls = bot.db.get_nick_value(bomb['target'], 'bomb_alls') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_alls', alls + 1) elif wirecut.capitalize() not in bomb['wires']: bot.say(STRINGS['CUT_IMAGINARY'] % target) # Add the target back onto the bomb list BOMBS[target.lower()] = bomb elif wirecut.capitalize() == bomb['color']: bot.say(STRINGS['CUT_CORRECT'] % target) bomb['timer'].cancel() # defuse bomb defuses = bot.db.get_nick_value(bomb['target'], 'bomb_defuses') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_defuses', defuses + 1) else: bomb['timer'].cancel() # defuse timer, execute premature detonation bot.say(STRINGS['CUT_WRONG'] % bomb['color']) kickboom(bot, trigger, target, bomb['bomber']) wrongs = bot.db.get_nick_value(bomb['target'], 'bomb_wrongs') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_wrongs', wrongs + 1)
def cutwire(bot, trigger): """ Tells sopel to cut a wire when you've been bombed. """ global BOMBS target = Identifier(trigger.nick) if target == bot.nick: # a parallel bot behind a bouncer (e.g. Bucket) can trigger this function (see #16) return with lock: if target.lower() != bot.nick.lower() and target.lower() not in BOMBS: bot.say(STRINGS['CUT_NO_BOMB'] % target) return if not trigger.group(3): bot.say(STRINGS['CUT_NO_WIRE']) return # Remove target from bomb list temporarily bomb = BOMBS.pop(target.lower()) wirecut = trigger.group(3) if wirecut.lower() in ('all', 'all!'): bomb['timer'].cancel() # defuse timer, execute premature detonation bot.say(STRINGS['CUT_ALL_WIRES'] % bomb['color']) kickboom(bot, trigger, target) alls = bot.db.get_nick_value(bomb['target'], 'bomb_alls') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_alls', alls + 1) elif wirecut.capitalize() not in bomb['wires']: bot.say(STRINGS['CUT_IMAGINARY'] % target) # Add the target back onto the bomb list BOMBS[target.lower()] = bomb elif wirecut.capitalize() == bomb['color']: bot.say(STRINGS['CUT_CORRECT'] % target) bomb['timer'].cancel() # defuse bomb defuses = bot.db.get_nick_value(bomb['target'], 'bomb_defuses') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_defuses', defuses + 1) else: bomb['timer'].cancel() # defuse timer, execute premature detonation bot.say(STRINGS['CUT_WRONG'] % bomb['color']) kickboom(bot, trigger, target) wrongs = bot.db.get_nick_value(bomb['target'], 'bomb_wrongs') or 0 bot.db.set_nick_value(bomb['target'], 'bomb_wrongs', wrongs + 1)
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: raise ValueError('Alias already exists.')
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. {0}'.format(e))
def adjust_nick_value(self, nick, key, value): """Adjusts the value for a given key to be associated with the nick.""" nick = Identifier(nick) result = self.execute( 'SELECT value FROM nicknames JOIN nick_values ' 'ON nicknames.nick_id = nick_values.nick_id ' 'WHERE slug = ? AND key = ?', [nick.lower(), key]).fetchone() if result is not None: result = result[0] current_value = _deserialize(result) value = current_value + value 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 get_nick_value(self, nick, key): """Retrieves the value for a given key associated with a nick.""" nick = Identifier(nick) session = self.ssession() try: result = session.query(NickValues) \ .filter(Nicknames.nick_id == NickValues.nick_id) \ .filter(Nicknames.slug == nick.lower()) \ .filter(NickValues.key == key) \ .one_or_none() if result is not None: result = result.value return _deserialize(result) except SQLAlchemyError: session.rollback() raise finally: session.close()
def test_get_nick_id(db: SopelDB): """Test get_nick_id does not create NickID by default.""" nick = Identifier('MrEricPraline') # Attempt to get nick ID: it is not created by default with pytest.raises(ValueError): db.get_nick_id(nick) # Create the nick ID nick_id = db.get_nick_id(nick, create=True) # Check that one and only one nickname exists with that ID with db.session() as session: nickname = session.execute( select(Nicknames).where(Nicknames.nick_id == nick_id)).scalar_one( ) # will raise if not one and exactly one assert nickname.canonical == 'MrEricPraline' assert nickname.slug == nick.lower()
def test_get_nick_id(db): """Test get_nick_id does not create NickID by default.""" nick = Identifier('Exirel') session = db.session() # Attempt to get nick ID: it is not created by default with pytest.raises(ValueError): db.get_nick_id(nick) # Create the nick ID nick_id = db.get_nick_id(nick, create=True) # Check that one and only one nickname exists with that ID nickname = session.query(Nicknames).filter( Nicknames.nick_id == nick_id, ).one() # will raise if not one and exactly one assert nickname.canonical == 'Exirel' assert nickname.slug == nick.lower() session.close()
def get_nick_value(self, nick, key, default=None): """Get a value from the key-value store for ``nick``. :param str nick: the nickname whose values to access :param str key: the name by which the desired value was saved :param mixed default: value to return if ``key`` does not have a value set (optional) :raise ~sqlalchemy.exc.SQLAlchemyError: if there is a database error .. versionadded:: 7.0 The ``default`` parameter. .. seealso:: To set a value for later retrieval with this method, use :meth:`set_nick_value`. To delete a value instead of retrieving it, use :meth:`delete_nick_value`. """ nick = Identifier(nick) session = self.ssession() try: result = session.query(NickValues) \ .filter(Nicknames.nick_id == NickValues.nick_id) \ .filter(Nicknames.slug == nick.lower()) \ .filter(NickValues.key == key) \ .one_or_none() if result is not None: result = result.value elif default is not None: result = default return _deserialize(result) except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
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) session = self.ssession() try: count = session.query(Nicknames) \ .filter(Nicknames.nick_id == nick_id) \ .count() if count <= 1: raise ValueError('Given alias is the only entry in its group.') session.query(Nicknames).filter(Nicknames.slug == alias.lower()).delete() session.commit() except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
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) session = self.ssession() try: count = session.query(Nicknames) \ .filter(Nicknames.nick_id == nick_id) \ .count() if count <= 1: raise ValueError('Given alias is the only entry in its group.') session.query(Nicknames).filter(Nicknames.slug == alias.lower()).delete() session.commit() except SQLAlchemyError: session.rollback() raise finally: session.close()
def get_channel_slug(self, chan): """Return the case-normalized representation of ``channel``. :param str channel: the channel name to normalize, with prefix (required) :return str: the case-normalized channel name (or "slug" representation) This is useful to make sure that a channel name is stored consistently in both the bot's own database and third-party plugins' databases/files, without regard for variation in case between different clients and/or servers on the network. """ chan = Identifier(chan) slug = chan.lower() session = self.ssession() try: count = session.query(ChannelValues) \ .filter(ChannelValues.channel == slug) \ .count() if count == 0: # see if it needs case-mapping migration old_rows = session.query(ChannelValues) \ .filter(ChannelValues.channel == Identifier._lower_swapped(chan)) old_count = old_rows.count() if old_count > 0: # it does! old_rows.update({ChannelValues.channel: slug}) session.commit() return slug except SQLAlchemyError: session.rollback() raise finally: self.ssession.remove()
def luv_h8(bot, trigger, target, which, warn_nonexistent=True): target = Identifier(target) which = which.lower() # issue #18 pfx = change = selfreply = None # keep PyCharm & other linters happy if target.lower() not in bot.privileges[trigger.sender.lower()]: if warn_nonexistent: bot.reply("You can only %s someone who is here." % which) return if rep_too_soon(bot, trigger.nick): return if which == 'luv': selfreply = "No narcissism allowed!" pfx, change = 'in', 1 if which == 'h8': selfreply = "Go to 4chan if you really hate yourself!" pfx, change = 'de', -1 if not (pfx and change and selfreply): # safeguard against leaving something in the above mass-None assignment bot.say("Logic error! Please report this to %s." % bot.config.core.owner) return if is_self(bot, trigger.nick, target): bot.reply(selfreply) return rep = mod_rep(bot, trigger.nick, target, change) bot.say("%s has %screased %s's reputation score to %d" % (trigger.nick, pfx, target, rep))
def start(bot, trigger): """ Put a bomb in the specified user's pants. If they take too long or guess wrong, they "die" and (if enabled) get kicked from the channel. """ if not trigger.group(3): bot.say(STRINGS['TARGET_MISSING']) return NOLIMIT if not bombing_allowed(bot, trigger.sender): bot.notice(STRINGS['CHANNEL_DISABLED'] % trigger.sender, trigger.nick) return NOLIMIT since_last = time_since_bomb(bot, trigger.nick) if since_last < TIMEOUT: bot.notice(STRINGS['TIMEOUT_REMAINING'] % (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(STRINGS['TARGET_BOT']) return NOLIMIT if is_self(bot, trigger.nick, target): bot.say(STRINGS['TARGET_SELF'] % trigger.nick) return NOLIMIT if target.lower() not in bot.privileges[trigger.sender.lower()]: bot.say(STRINGS['TARGET_IMAGINARY']) return NOLIMIT if target_unbombable and not trigger.admin: bot.say(STRINGS['TARGET_DISABLED'] % target) return NOLIMIT if bot.db.get_nick_value(trigger.nick, 'unbombable'): bot.say(STRINGS['NOT_WHILE_DISABLED'] % trigger.nick) return NOLIMIT with lock: if target.lower() in BOMBS: bot.say(STRINGS['TARGET_FULL'] % 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( choice(STRINGS['BOMB_PLANTED']) % {'target': target, 'fuse_time': STRINGS['FUSE'], 'wire_num': num_wires, 'wire_list': wires_list, 'prefix': bot.config.core.help_prefix or '.' }) bot.notice(STRINGS['BOMB_ANSWER'] % (target, color), trigger.nick) if target_unbombable: bot.notice(STRINGS['TARGET_DISABLED_FYI'] % target, trigger.nick) timer = Timer(FUSE, explode, (bot, trigger)) BOMBS[target.lower()] = {'wires': wires, 'color': color, 'timer': timer, 'target': target, 'bomber': trigger.nick } 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 explode(bot, trigger): target = Identifier(trigger.group(3)) kmsg = 'KICK ' + trigger.sender + ' ' + target + \ ' : Oh, come on, ' + target + '! You could\'ve at least picked one! Now you\'re dead. Guts, all over the place. You see that? Guts, all over YourPants. (You should\'ve picked the ' + bombs[target.lower()][0] + ' wire.)' bot.write([kmsg]) bombs.pop(target.lower())
def start(bot, trigger): """ Put a bomb in the specified user's pants. If they take too long or guess wrong, they die (and get kicked from the channel, if enabled). """ if not trigger.group(3): bot.say(STRINGS['TARGET_MISSING']) return NOLIMIT if bot.db.get_channel_value(trigger.sender, 'bombs_disabled'): bot.notice(STRINGS['CHANNEL_DISABLED'] % trigger.sender, trigger.nick) return NOLIMIT since_last = time_since_bomb(bot, trigger.nick) if since_last < TIMEOUT and not trigger.admin: bot.notice(STRINGS['TIMEOUT_REMAINING'] % (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(STRINGS['TARGET_BOT']) return NOLIMIT if is_self(bot, trigger.nick, target): bot.say(STRINGS['TARGET_SELF'] % trigger.nick) return NOLIMIT if target.lower() not in bot.privileges[trigger.sender.lower()]: bot.say(STRINGS['TARGET_IMAGINARY']) return NOLIMIT if target_unbombable and not trigger.admin: bot.say(STRINGS['TARGET_DISABLED'] % target) return NOLIMIT if bot.db.get_nick_value(trigger.nick, 'unbombable'): bot.say(STRINGS['NOT_WHILE_DISABLED'] % trigger.nick) return NOLIMIT with lock: if target.lower() in BOMBS: bot.say(STRINGS['TARGET_FULL'] % 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( choice(STRINGS['BOMB_PLANTED']) % {'target': target, 'fuse_time': STRINGS['FUSE'], 'wire_num': num_wires, 'wire_list': wires_list, 'prefix': bot.config.core.help_prefix or '.' }) bot.notice(STRINGS['BOMB_ANSWER'] % (target, color), trigger.nick) if target_unbombable: bot.notice(STRINGS['TARGET_DISABLED_FYI'] % target, trigger.nick) timer = Timer(FUSE, explode, (bot, trigger)) BOMBS[target.lower()] = {'wires': wires, 'color': color, 'timer': timer, 'target': target, 'bomber': trigger.nick } 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())