Exemplo n.º 1
0
def track_nicks(bot, trigger):
    '''Track nickname changes and maintain our chanops list accordingly'''
    old = trigger.nick
    new = Nick(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."
        bot.debug(__file__, debug_msg, 'always')
        bot.msg(bot.config.core.owner, privmsg)
        return

    for channel in bot.privileges:
        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)
Exemplo n.º 2
0
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 = Nick(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 %s at %s on %s, saying %s" % (nick, timestamp,
                                                        channel, message)
        bot.say(str(trigger.nick) + ': ' + msg)
    else:
        bot.say("Sorry, I haven't seen %s around." % nick)
Exemplo n.º 3
0
def take_comment(bot, trigger):
    """
    Log a comment, to be shown with other comments when a chair uses .comments.
    Intended to allow commentary from those outside the primary group of people
    in the meeting.

    Used in private message only, as `.comment <#channel> <comment to add>`
    https://github.com/embolalia/willie/wiki/Using-the-meetbot-module
    """
    target, message = trigger.group(2).split(None, 1)
    target = Nick(target)
    if not trigger.sender.is_nick():
        return
    if not ismeetingrunning(target):
        bot.say("There's not currently a meeting in that channel.")
    else:
        meetings_dict[trigger.group(3)]['comments'].append(
            (trigger.nick, message))
        bot.say("Your comment has been recorded. It will be shown when the"
                " chairs tell me to show the comments.")
        bot.msg(meetings_dict[trigger.group(3)]['head'],
                "A new comment has been recorded.")
Exemplo n.º 4
0
def handle_names(bot, trigger):
    ''' Handle NAMES response, happens when joining to channels'''
    names = re.split(' ', trigger)
    channels = re.search('(#\S*)', bot.raw)
    if (channels is None):
        return
    channel = channels.group(1)
    if channel not in bot.privileges:
        bot.privileges[channel] = dict()
    bot.init_ops_list(channel)
    for name in names:
        priv = 0
        # This could probably be made flexible in the future, but I don't think
        # it'd be worht it.
        mapping = {
            '+': willie.module.VOICE,
            '%': willie.module.HALFOP,
            '@': willie.module.OP,
            '&': willie.module.ADMIN,
            '~': willie.module.OWNER
        }
        for prefix, value in mapping.iteritems():
            if prefix in name:
                priv = priv | value
        nick = Nick(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv

        # Old op list maintenance is down here, and should be removed at some
        # point
        if '@' in name or '~' in name or '&' in name:
            bot.add_op(channel, name.lstrip('@&%+~'))
            bot.add_halfop(channel, name.lstrip('@&%+~'))
            bot.add_voice(channel, name.lstrip('@&%+~'))
        elif '%' in name:
            bot.add_halfop(channel, name.lstrip('@&%+~'))
            bot.add_voice(channel, name.lstrip('@&%+~'))
        elif '+' in name:
            bot.add_voice(channel, name.lstrip('@&%+~'))
Exemplo n.º 5
0
def kick(bot, trigger):
    """
    Kick a user from the channel.
    """
    if bot.privileges[trigger.sender][trigger.nick] < OP:
        return
    text = trigger.group().split()
    argc = len(text)
    if argc < 2:
        return
    opt = Nick(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])
Exemplo n.º 6
0
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
    text = trigger.group().split()
    argc = len(text)
    if argc < 2:
        return
    opt = Nick(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])
Exemplo n.º 7
0
def unban(bot, trigger):
    """
    This give admins the ability to unban a user.
    The bot must be a Channel Operator for this command to work.
    """
    if bot.privileges[trigger.sender][trigger.nick] < OP:
        return
    text = trigger.group().split()
    argc = len(text)
    if argc < 2:
        return
    opt = Nick(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])
Exemplo n.º 8
0
def tweet_links_auto(willie, trigger):
    if trigger.nick in willie.config.core.nick_blocks:
        return
    if trigger.startswith('.title ') or trigger.startswith('.tweet'):
        return
    if len(re.findall("\([\d]+\sfiles\sin\s[\d]+\sdirs\)", trigger)) == 1:
        return

    if not optin_check(willie, Nick(trigger.nick)):
        return

    try:
        results = get_results(willie, trigger)
    except Exception as e:
        raise e
    if results is None: return

    k = 1
    for r in results:
        if k > 3: break
        k += 1

        tweet(willie, trigger, r[0], r[1])
Exemplo n.º 9
0
def findandreplace(bot, trigger):
    # Don't bother in PM
    if not trigger.sender.startswith('#'):
        return

    # Correcting other person vs self.
    rnick = Nick(trigger.group(1) or trigger.nick)

    search_dict = bot.memory['find_lines']
    # only do something if there is conversation to work with
    if trigger.sender not in search_dict:
        return
    if Nick(rnick) not in search_dict[trigger.sender]:
        return

    #TODO rest[0] is find, rest[1] is replace. These should be made variables of
    #their own at some point.
    rest = [trigger.group(2), trigger.group(3)]
    rest[0] = rest[0].replace(r'\/', '/')
    rest[1] = rest[1].replace(r'\/', '/')
    me = False  # /me command
    flags = (trigger.group(4) or '')

    # If g flag is given, replace all. Otherwise, replace once.
    if 'g' in flags:
        count = -1
    else:
        count = 1

    # repl is a lambda function which performs the substitution. i flag turns
    # off case sensitivity. re.U turns on unicode replacement.
    if 'i' in flags:
        regex = re.compile(re.escape(rest[0]), re.U | re.I)
        repl = lambda s: re.sub(regex, rest[1], s, count == 1)
    else:
        repl = lambda s: s.replace(rest[0], rest[1], count)

    # Look back through the user's lines in the channel until you find a line
    # where the replacement works
    for line in reversed(search_dict[trigger.sender][rnick]):
        if line.startswith("\x01ACTION"):
            me = True  # /me command
            line = line[8:]
        else:
            me = False
        new_phrase = repl(line)
        if new_phrase != line:  # we are done
            break

    if not new_phrase or new_phrase == line:
        return  # Didn't find anything

    # Save the new "edited" message.
    action = (me and '\x01ACTION ') or ''  # If /me message, prepend \x01ACTION
    templist = search_dict[trigger.sender][rnick]
    templist.append(action + new_phrase)
    search_dict[trigger.sender][rnick] = templist
    bot.memory['find_lines'] = search_dict

    # output
    if not me:
        if bot.config.lang == 'ca':
            new_phrase = '\x02volia dir:\x02 ' + new_phrase
        elif bot.config.lang == 'es':
            new_phrase = u'\x02quería decir:\x02 ' + new_phrase
        else:
            new_phrase = '\x02meant to say:\x02 ' + new_phrase
    if trigger.group(1):
        phrase = '* %s %s %s' % (trigger.nick, rnick, new_phrase)
    else:
        phrase = '%s %s' % (trigger.nick, new_phrase)

    bot.say(phrase)
Exemplo n.º 10
0
def note(bot, trigger):
    if not trigger.is_privmsg:
        nick = Nick(trigger.nick)
        seen_dict[nick]['timestamp'] = time.time()
        seen_dict[nick]['channel'] = trigger.sender
        seen_dict[nick]['message'] = trigger
Exemplo n.º 11
0
def track_modes(bot, trigger):
    """Track usermode changes and keep our lists of ops up to date."""
    # Mode message format: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
    channel = Nick(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 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 = Nick(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)
Exemplo n.º 12
0
 def del_halfop(self, channel, name):
     self.halfplus[channel].discard(Nick(name))
Exemplo n.º 13
0
 def del_voice(self, channel, name):
     self.voices[channel].discard(Nick(name))
Exemplo n.º 14
0
 def add_voice(self, channel, name):
     if isinstance(name, Nick):
         self.voices[channel].add(name)
     else:
         self.voices[channel].add(Nick(name))
Exemplo n.º 15
0
 def del_op(self, channel, name):
     self.ops[channel].discard(Nick(name))
Exemplo n.º 16
0
 def add_op(self, channel, name):
     if isinstance(name, Nick):
         self.ops[channel].add(name)
     else:
         self.ops[channel].add(Nick(name))
Exemplo n.º 17
0
 def add_halfop(self, channel, name):
     if isinstance(name, Nick):
         self.halfplus[channel].add(name)
     else:
         self.halfplus[channel].add(Nick(name))
Exemplo n.º 18
0
    def __init__(self, config):
        ca_certs = '/etc/pki/tls/cert.pem'
        if config.ca_certs is not None:
            ca_certs = config.ca_certs
        elif not os.path.isfile(ca_certs):
            ca_certs = '/etc/ssl/certs/ca-certificates.crt'
        if not os.path.isfile(ca_certs):
            stderr('Could not open CA certificates file. SSL will not '
                   'work properly.')

        if config.log_raw is None:
            # Default is to log raw data, can be disabled in config
            config.log_raw = True
        asynchat.async_chat.__init__(self)
        self.set_terminator(b'\n')
        self.buffer = ''

        self.nick = Nick(config.nick)
        """Willie's current ``Nick``. Changing this while Willie is running is
        untested."""
        self.user = config.user
        """Willie's user/ident."""
        self.name = config.name
        """Willie's "real name", as used for whois."""

        self.channels = []
        """The list of channels Willie is currently in."""

        self.stack = {}
        self.ca_certs = ca_certs
        self.hasquit = False

        self.sending = threading.RLock()
        self.writing_lock = threading.Lock()
        self.raw = None

        # Right now, only accounting for two op levels.
        # This might be expanded later.
        # These lists are filled in startup.py, as of right now.
        self.ops = dict()
        """
        A dictionary mapping channels to a ``Nick`` list of their operators.
        """
        self.halfplus = dict()
        """
        A dictionary mapping channels to a ``Nick`` list of their half-ops and
        ops.
        """
        self.voices = dict()
        """
        A dictionary mapping channels to a ``Nick`` list of their voices,
        half-ops and ops.
        """

        # We need this to prevent error loops in handle_error
        self.error_count = 0

        self.connection_registered = False
        """ Set to True when a server has accepted the client connection and
        messages can be sent and received. """

        # Work around bot.connecting missing in Python older than 2.7.4
        if not hasattr(self, "connecting"):
            self.connecting = False
Exemplo n.º 19
0
def note(willie, trigger):
    if trigger.sender.startswith('#'):
        nick = Nick(trigger.nick)
        seen_dict[nick]['timestamp'] = time.time()
        seen_dict[nick]['channel'] = trigger.sender
        seen_dict[nick]['message'] = trigger
Exemplo n.º 20
0
def findandreplace(bot, trigger):
    # Don't bother in PM
    if trigger.is_privmsg:
        return

    # Correcting other person vs self.
    rnick = Nick(trigger.group(1) or trigger.nick)

    search_dict = bot.memory['find_lines']
    # only do something if there is conversation to work with
    if trigger.sender not in search_dict:
        return
    if Nick(rnick) not in search_dict[trigger.sender]:
        return

    FLAGS = {
        'i': re.I,
    }

    pattern = trigger.group(2)
    replacement = trigger.group(3)
    me = False  # /me command

    flags = (trigger.group(4) or '')

    # If g flag is given, replace all. Otherwise, replace once.
    count = 0 if 'g' in flags else 1

    flags = reduce(operator.add, (FLAGS.get(f, 0) for f in flags), re.U)

    def replacer(match):
        def replacer(m):
            if m.group(0) == '&':
                return match.group(0)

            if m.group(1).isdigit():
                try:
                    return match.group(int(m.group(1)))
                except IndexError:
                    return ''

            return m.group(1)

        return re.sub(r'\\(.)|&', replacer, replacement)

    repl = lambda s: re.sub(pattern, replacer, s, count)

    # Look back through the user's lines in the channel until you find a line
    # where the replacement works
    for line in reversed(search_dict[trigger.sender][rnick]):
        if line.startswith("\x01ACTION"):
            me = True  # /me command
            line = line[8:]
        else:
            me = False
        new_phrase = repl(line)
        if new_phrase != line:  # we are done
            break

    if not new_phrase or new_phrase == line:
        return  # Didn't find anything

    # Save the new "edited" message.
    action = (me and '\x01ACTION ') or ''  # If /me message, prepend \x01ACTION
    templist = search_dict[trigger.sender][rnick]
    templist.append(action + new_phrase)
    search_dict[trigger.sender][rnick] = templist
    bot.memory['find_lines'] = search_dict

    # output
    if not me:
        new_phrase = '\x02meant\x02 to say: ' + new_phrase
    if trigger.group(1):
        phrase = '%s thinks %s %s' % (trigger.nick, rnick, new_phrase)
    else:
        phrase = '%s %s' % (trigger.nick, new_phrase)

    bot.say(phrase)
Exemplo n.º 21
0
def track_modes(bot, trigger):
    ''' Track usermode changes and keep our lists of ops up to date '''
    line = trigger.args

    # 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 line[0][0] != '#':
        return
    channel, mode_sec = line[:2]
    nicks = [Nick(n) for n in line[2:]]

    # Break out the modes, because IRC allows e.g. MODE +aB-c foo bar baz
    sign = ''
    modes = []
    for char in mode_sec:
        if char == '+' or char == '-':
            sign = char
        else:
            modes.append(sign + char)

    # Some basic checks for broken replies from server. Probably unnecessary.
    if len(modes) > len(nicks):
        bot.debug(__file__,
                  'MODE recieved from server with more modes than nicks.',
                  'warning')
        modes = modes[:(len(nicks) + 1)]  # Try truncating, in case that works.
    elif len(modes) < len(nicks):
        bot.debug(__file__,
                  'MODE recieved from server with more nicks than modes.',
                  'warning')
        nicks = nicks[:(len(modes) - 1)]  # Try truncating, in case that works.
    # This one is almost certainly unneeded.
    if not (len(modes) and len(nicks)):
        bot.debug(__file__, 'MODE recieved from server without arguments',
                  'verbose')
        return  # Nothing to do here.

    mapping = {
        'v': willie.module.VOICE,
        'h': willie.module.HALFOP,
        'o': willie.module.OP,
        'a': willie.module.ADMIN,
        'q': willie.module.OWNER
    }
    for nick, mode in zip(nicks, modes):
        priv = bot.privileges[channel].get(nick) or 0
        value = mapping.get(mode[1])
        if value is not None:
            priv = priv | value
            bot.privileges[channel][nick] = priv

        #Old mode maintenance
        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)
Exemplo n.º 22
0
        def __new__(cls, text, origin, bytes, match, event, args, self):
            s = unicode.__new__(cls, text)
            """Is trigger from a channel or in PM"""
            s.is_privmsg = origin.sender.is_nick()

            s.sender = origin.sender
            """
            The channel (or nick, in a private message) from which the
            message was sent.
            """
            s.hostmask = origin.hostmask
            """
            Hostmask of the person who sent the message in the form
            <nick>!<user>@<host>
            """
            s.user = origin.user
            """Local username of the person who sent the message"""
            s.nick = origin.nick
            """The ``Nick`` of the person who sent the message."""
            s.event = event
            """
            The IRC event (e.g. ``PRIVMSG`` or ``MODE``) which triggered the
            message."""
            s.bytes = bytes
            """
            The text which triggered the message. Equivalent to
            ``Trigger.group(0)``.
            """
            s.match = match
            """
            The regular expression ``MatchObject_`` for the triggering line.
            .. _MatchObject: http://docs.python.org/library/re.html#match-objects
            """
            s.group = match.group
            """The ``group`` function of the ``match`` attribute.

            See Python ``re_`` documentation for details."""
            s.groups = match.groups
            """The ``groups`` function of the ``match`` attribute.

            See Python ``re_`` documentation for details."""
            s.args = args
            """
            A tuple containing each of the arguments to an event. These are the
            strings passed between the event name and the colon. For example,
            setting ``mode -m`` on the channel ``#example``, args would be
            ``('#example', '-m')``
            """
            s.tags = origin.tags
            """A map of the IRCv3 message tags on the message.

            If the message had no tags, or the server does not support IRCv3
            message tags, this will be an empty dict."""
            if len(self.config.core.get_list('admins')) > 0:
                s.admin = (origin.nick in [
                    Nick(n) for n in self.config.core.get_list('admins')
                ])
            else:
                s.admin = False
            """
            True if the nick which triggered the command is in Willie's admin
            list as defined in the config file.
            """

            # Support specifying admins by hostnames
            if not s.admin and len(self.config.core.get_list('admins')) > 0:
                for each_admin in self.config.core.get_list('admins'):
                    re_admin = re.compile(each_admin)
                    if re_admin.findall(origin.host):
                        s.admin = True
                    elif '@' in each_admin:
                        temp = each_admin.split('@')
                        re_host = re.compile(temp[1])
                        if re_host.findall(origin.host):
                            s.admin = True

            if not self.config.core.owner:
                s.owner = False
            elif '@' in self.config.core.owner:
                s.owner = origin.nick + '@' + \
                    origin.host == self.config.core.owner
            else:
                s.owner = (origin.nick == Nick(self.config.core.owner))

            # Bot owner inherits all the admin rights, therefore is considered
            # admin
            s.admin = s.admin or s.owner

            s.host = origin.host
            if s.sender is not s.nick:  # no ops in PM
                s.ops = self.ops.get(s.sender, [])
                """
                List of channel operators in the channel the message was
                recived in
                """
                s.halfplus = self.halfplus.get(s.sender, [])
                """
                List of channel half-operators in the channel the message was
                recived in
                """
                s.isop = (s.nick in s.ops or s.nick in s.halfplus)
                """True if the user is half-op or an op"""
                s.voices = self.voices.get(s.sender, [])
                """
                List of channel operators in the channel the message was
                recived in
                """
                s.isvoice = (s.nick in s.ops or s.nick in s.halfplus
                             or s.nick in s.voices)
                """True if the user is voiced, has op, or has half-op"""
            else:
                s.isop = False
                s.isvoice = False
                s.ops = []
                s.halfplus = []
                s.voices = []
            return s
Exemplo n.º 23
0
def blocks(bot, trigger):
    """
    Manage Willie's blocking features.
    https://github.com/embolalia/willie/wiki/Making-Willie-ignore-people
    """
    if not trigger.admin:
        return

    STRINGS = {
        "success_del": "Successfully deleted block: %s",
        "success_add": "Successfully added block: %s",
        "no_nick": "No matching nick block found for: %s",
        "no_host": "No matching hostmask block found for: %s",
        "invalid":
        "Invalid format for %s a block. Try: .blocks add (nick|hostmask) willie",
        "invalid_display": "Invalid input for displaying blocks.",
        "nonelisted": "No %s listed in the blocklist.",
        'huh': "I could not figure out what you wanted to do.",
    }

    masks = bot.config.core.get_list('host_blocks')
    nicks = [Nick(nick) for nick in bot.config.core.get_list('nick_blocks')]
    print masks, nicks
    text = trigger.group().split()

    if len(text) == 3 and text[1] == "list":
        if text[2] == "hostmask":
            if len(masks) > 0 and masks.count("") == 0:
                for each in masks:
                    if len(each) > 0:
                        bot.say("blocked hostmask: " + each)
            else:
                bot.reply(STRINGS['nonelisted'] % ('hostmasks'))
        elif text[2] == "nick":
            if len(nicks) > 0 and nicks.count("") == 0:
                for each in nicks:
                    if len(each) > 0:
                        bot.say("blocked nick: " + each)
            else:
                bot.reply(STRINGS['nonelisted'] % ('nicks'))
        else:
            bot.reply(STRINGS['invalid_display'])

    elif len(text) == 4 and text[1] == "add":
        if text[2] == "nick":
            nicks.append(text[3])
            bot.config.core.nick_blocks = nicks
            bot.config.save()
        elif text[2] == "hostmask":
            masks.append(text[3].lower())
            bot.config.core.host_blocks = masks
        else:
            bot.reply(STRINGS['invalid'] % ("adding"))
            return

        bot.reply(STRINGS['success_add'] % (text[3]))

    elif len(text) == 4 and text[1] == "del":
        if text[2] == "nick":
            try:
                nicks.remove(Nick(text[3]))
                bot.config.core.nick_blocks = nicks
                bot.config.save()
                bot.reply(STRINGS['success_del'] % (text[3]))
            except:
                bot.reply(STRINGS['no_nick'] % (text[3]))
                return
        elif text[2] == "hostmask":
            try:
                masks.remove(text[3].lower())
                bot.config.core.host_blocks = masks
                bot.config.save()
                bot.reply(STRINGS['success_del'] % (text[3]))
            except:
                bot.reply(STRINGS['no_host'] % (text[3]))
                return
        else:
            bot.reply(STRINGS['invalid'] % ("deleting"))
            return
    else:
        bot.reply(STRINGS['huh'])
Exemplo n.º 24
0
    def msg(self, recipient, text, max_messages=1):
        # We're arbitrarily saying that the max is 400 bytes of text when
        # messages will be split. Otherwise, we'd have to acocunt for the bot's
        # hostmask, which is hard.
        max_text_length = 400
        # Encode to bytes, for propper length calculation
        if isinstance(text, unicode):
            encoded_text = text.encode('utf-8')
        else:
            encoded_text = text
        excess = ''
        if max_messages > 1 and len(encoded_text) > max_text_length:
            last_space = encoded_text.rfind(' '.encode('utf-8'), 0,
                                            max_text_length)
            if last_space == -1:
                excess = encoded_text[max_text_length:]
                encoded_text = encoded_text[:max_text_length]
            else:
                excess = encoded_text[last_space + 1:]
                encoded_text = encoded_text[:last_space]
        # We'll then send the excess at the end
        # Back to unicode again, so we don't screw things up later.
        text = encoded_text.decode('utf-8')
        try:
            self.sending.acquire()

            # No messages within the last 3 seconds? Go ahead!
            # Otherwise, wait so it's been at least 0.8 seconds + penalty

            recipient_id = Nick(recipient)

            if recipient_id not in self.stack:
                self.stack[recipient_id] = []
            elif self.stack[recipient_id]:
                elapsed = time.time() - self.stack[recipient_id][-1][0]
                if elapsed < 3:
                    penalty = float(max(0, len(text) - 50)) / 70
                    wait = 0.7 + penalty
                    if elapsed < wait:
                        time.sleep(wait - elapsed)

                # Loop detection
                messages = [m[1] for m in self.stack[recipient_id][-8:]]

                # If what we about to send repeated at least 5 times in the
                # last 2 minutes, replace with '...'
                if messages.count(text) >= 5 and elapsed < 120:
                    text = '...'
                    if messages.count('...') >= 3:
                        # If we said '...' 3 times, discard message
                        return

            self.write(('PRIVMSG', recipient), text)
            self.stack[recipient_id].append((time.time(), self.safe(text)))
            self.stack[recipient_id] = self.stack[recipient_id][-10:]
        finally:
            self.sending.release()
        # Now that we've sent the first part, we need to send the rest. Doing
        # this recursively seems easier to me than iteratively
        if excess:
            self.msg(recipient, excess, max_messages - 1)