Пример #1
0
def trigger_MODE(code, line):
    re_tmp = r'^\:(.*?)\!(.*?)\@(.*?) MODE (.*?)$'
    try:
        nick, ident, host, args = re.compile(re_tmp).match(line).groups()
    except:
        return
    output.normal('%s sets MODE %s' % (nick, args), 'MODE')

    # Stuff for user_list
    data = line.split('MODE', 1)[1]
    if len(data.split()) >= 3:
        channel, modes, users = data.strip().split(' ', 2)
        users = users.split()
        tmp = []

        def remove(old, sign):
            tmp = []
            modes = []
            for char in old:
                modes.append(char)
            while sign in modes:
                i = modes.index(sign)
                tmp.append(i)
                del modes[i]
            return tmp, ''.join(modes)

        if modes.startswith('+'):
            _plus, new_modes = remove(modes, '+')
            _minus, new_modes = remove(new_modes, '-')
        else:
            _minus, new_modes = remove(modes, '-')
            _plus, new_modes = remove(new_modes, '+')

        for index in range(len(users)):
            _usr = users[index]
            _mode = new_modes[index]
            _sign = ''
            if index in _plus:
                _sign = '+'
            if index in _minus:
                _sign = '-'
            tmp.append({'name': _usr, 'mode': _mode, 'sign': _sign})

        last_used = ''

        for index in range(len(tmp)):
            if not last_used:
                last_used = tmp[index]['sign']
            if not tmp[index]['sign'] or len(tmp[index]['sign']) == 0:
                tmp[index]['sign'] = last_used
            else:
                last_used = tmp[index]['sign']

        names = {'v': 'voiced', 'o': 'op', '+': True, '-': False}
        for user in tmp:
            if user['mode'] in names and user['sign'] in names:
                mode, name, sign = names[user['mode']], user['name'], names[user['sign']]
                code.chan[channel][name][mode] = sign
                if mode == 'op' and sign:
                    code.chan[channel][name]['voiced'] = True
Пример #2
0
def trigger_NOTICE(code, origin, line, args, text):
    """
        ID:         NOTICE
        Decription: The NOTICE message is used similarly to PRIVMSG. The difference
                    between NOTICE and PRIVMSG is that automatic replies must never be
                    sent in response to a NOTICE message. This rule applies to servers
                    too - they must not send any error reply back to the client on
                    receipt of a notice.
        Format:     <nickname> <text>
    """

    if 'Invalid password for ' in text:
        if not code.debug:
            output.error('Invalid NickServ password')
        os._exit(1)
    if 'AUTHENTICATION SUCCESSFUL as ' in args[2]:
        if code.config('undernet_hostmask'):
            code.write(('MODE', code.nick, '+x'))
    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
    # Add notices to the bot logs
    tmp = {
        'message': text,
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NOTICE'
    }
    code.logs['bot'].append(tmp)
Пример #3
0
def trigger_NICK(code, origin, line, args, text):
    """
        ID:         NICK
        Decription: NICK message is used to give user a nickname or change the previous
                    one. The <hopcount> parameter is only used by servers to indicate
                    how far away a nick is from its home server. A local connection has
                    a hopcount of 0. If supplied by a client, it must be ignored.

        Format:     <nickname> [ <hopcount> ]
        Numeric Replies:
           - ERR_NONICKNAMEGIVEN
           - ERR_ERRONEUSNICKNAME
           - ERR_NICKNAMEINUSE
           - ERR_NICKCOLLISION
    """

    if not code.debug:
        output.normal('{} is now known as {}'.format(origin.nick, args[1]),
                      'NICK')

    # Rename old users to new ones in the database...
    for channel in code.chan:
        if origin.nick in code.chan[channel]:
            old = code.chan[channel][origin.nick]
            del code.chan[channel][origin.nick]
            code.chan[channel][args[1]] = old
            code.chan[channel][args[1]]['last_seen'] = int(time.time())

    tmp = {
        'message': 'is now known as {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NICK'
    }
    code.logs['bot'].append(tmp)
Пример #4
0
def trigger_PART(code, origin, line, args, text):
    """
        ID:         PART
        Decription: The PART message causes the client sending the message to be removed
                    from the list of active users for all given channels listed in the
                    parameter string.
        Format:     <channel>{,<channel>}
        Numeric Replies:
           - ERR_NEEDMOREPARAMS
           - ERR_NOSUCHCHANNEL
           - ERR_NOTONCHANNEL
    """

    if origin.nick == code.nick:
        del code.chan[args[1]]
        del code.logs['channel'][args[1]]
    else:
        del code.chan[args[1]][origin.nick]
    if len(args) == 3:
        reason = args[2]
    else:
        reason = 'Unknown'
    if not code.debug:
        output.normal(
            '{} has part {}. Reason: {}'.format(origin.nick, args[1], reason),
            args[1])
    tmp = {
        'message': 'left {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'PART'
    }
    code.logs['bot'].append(tmp)
Пример #5
0
def trigger_NICK(code, origin, line, args, text):
    """
        ID:         NICK
        Decription: NICK message is used to give user a nickname or change the previous
                    one. The <hopcount> parameter is only used by servers to indicate
                    how far away a nick is from its home server. A local connection has
                    a hopcount of 0. If supplied by a client, it must be ignored.

        Format:     <nickname> [ <hopcount> ]
        Numeric Replies:
           - ERR_NONICKNAMEGIVEN
           - ERR_ERRONEUSNICKNAME
           - ERR_NICKNAMEINUSE
           - ERR_NICKCOLLISION
    """

    if not code.debug:
        output.normal('{} is now known as {}'.format(origin.nick, args[1]), 'NICK')

    # Rename old users to new ones in the database...
    for channel in code.chan:
        if origin.nick in code.chan[channel]:
            old = code.chan[channel][origin.nick]
            del code.chan[channel][origin.nick]
            code.chan[channel][args[1]] = old
            code.chan[channel][args[1]]['last_seen'] = int(time.time())

    tmp = {
        'message': 'is now known as {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NICK'
    }
    code.logs['bot'].append(tmp)
Пример #6
0
def trigger_PART(code, origin, line, args, text):
    """
        ID:         PART
        Decription: The PART message causes the client sending the message to be removed
                    from the list of active users for all given channels listed in the
                    parameter string.
        Format:     <channel>{,<channel>}
        Numeric Replies:
           - ERR_NEEDMOREPARAMS
           - ERR_NOSUCHCHANNEL
           - ERR_NOTONCHANNEL
    """

    if origin.nick == code.nick:
        del code.chan[args[1]]
        del code.logs['channel'][args[1]]
    else:
        del code.chan[args[1]][origin.nick]
    if len(args) == 3:
        reason = args[2]
    else:
        reason = 'Unknown'
    if not code.debug:
        output.normal('{} has part {}. Reason: {}'.format(origin.nick, args[1], reason), args[1])
    tmp = {
        'message': 'left {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'PART'
    }
    code.logs['bot'].append(tmp)
Пример #7
0
def trigger_PRIVMSG(code, origin, line, args, text):
    text = code.stripcolors(text)
    if text.startswith('\x01ACTION'):
        text = '(me) ' + text.split(' ', 1)[1].strip('\x01')
    output.normal('({}) {}'.format(origin.nick, text), args[1])

    # Stuff for user_list
    if args[1].startswith('#'):
        if origin.nick not in code.chan[args[1]]:
            code.chan[args[1]][origin.nick] = {'normal': True, 'voiced':
                                               False, 'op': False, 'count': 0, 'messages': []}
        code.chan[args[1]][origin.nick]['count'] += 1
        # 1. per-channel-per-user message storing...
        code.chan[args[1]][origin.nick]['messages'].append(
            {'time': int(time.time()), 'message': text})
        # Ensure it's not more than 20 of the last messages
        code.chan[args[1]][origin.nick]['messages'] = code.chan[
            args[1]][origin.nick]['messages'][-20:]
        # 2. Per channel message storing...
        tmp = {'message': text, 'nick': origin.nick,
               'time': int(time.time()), 'channel': args[1]}
        code.logs['channel'][args[1]].append(tmp)
        code.logs['channel'][args[1]] = code.logs['channel'][args[1]][-20:]
        # 3. All bot messages in/out, maxed out by n * 100 (n being number of
        # channels)
        code.logs['bot'].append(tmp)
        code.logs['bot'] = code.logs['bot'][-(100 * len(code.channels)):]
Пример #8
0
def trigger_NOTICE(code, origin, line, args, text):
    """
        ID:         NOTICE
        Decription: The NOTICE message is used similarly to PRIVMSG. The difference
                    between NOTICE and PRIVMSG is that automatic replies must never be
                    sent in response to a NOTICE message. This rule applies to servers
                    too - they must not send any error reply back to the client on
                    receipt of a notice.
        Format:     <nickname> <text>
    """

    if 'Invalid password for ' in text:
        if not code.debug:
            output.error('Invalid NickServ password')
        os._exit(1)
    if 'AUTHENTICATION SUCCESSFUL as ' in args[2]:
        if code.config('undernet_hostmask'):
            code.write(('MODE', code.nick, '+x'))
    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
    # Add notices to the bot logs
    tmp = {
        'message': text,
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NOTICE'
    }
    code.logs['bot'].append(tmp)
Пример #9
0
 def initiate_connect(self, host, port):
     count = 0
     max_attempts = 5
     if hasattr(self.config, 'delay'):
         delay = int(self.config.delay)
     else:
         delay = 20
     while True:
         if count >= max_attempts:
             break
         try:
             count += 1
             if count > 1:
                 output.error(
                     'Failed to connect! Trying again in '
                     '%s seconds.' % str(delay)
                 )
                 time.sleep(delay)
             if self.verbose:
                 output.normal('Connecting to %s:%s... (try %s)' % (host, port, str(count)), 'STATUS')
             self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
             self.connect((host, port))
             count = 0
             asyncore.loop()
         except:
             pass
     output.error('Too many failed attempts. Exiting.')
     os._exit(1)
Пример #10
0
def trigger_MODE(code, origin, line, args, text):
    if len(args) == 3:
        output.normal('{} sets MODE {}'.format(origin.nick, text), 'MODE')
        return
    else:
        output.normal('{} sets MODE {}'.format(origin.nick, args[2]), args[1])

    # Stuff for user_list
    data = ' '.join(args[1:])

    channel, modes, users = data.strip().split(' ', 2)
    users = users.split()
    tmp = []

    def remove(old, sign):
        tmp = []
        modes = []
        for char in old:
            modes.append(char)
        while sign in modes:
            i = modes.index(sign)
            tmp.append(i)
            del modes[i]
        return tmp, ''.join(modes)

    if modes.startswith('+'):
        _plus, new_modes = remove(modes, '+')
        _minus, new_modes = remove(new_modes, '-')
    else:
        _minus, new_modes = remove(modes, '-')
        _plus, new_modes = remove(new_modes, '+')

    for index in range(len(users)):
        _usr = users[index]
        _mode = new_modes[index]
        _sign = ''
        if index in _plus:
            _sign = '+'
        if index in _minus:
            _sign = '-'
        tmp.append({'name': _usr, 'mode': _mode, 'sign': _sign})

    last_used = ''

    for index in range(len(tmp)):
        if not last_used:
            last_used = tmp[index]['sign']
        if not tmp[index]['sign'] or len(tmp[index]['sign']) == 0:
            tmp[index]['sign'] = last_used
        else:
            last_used = tmp[index]['sign']

    names = {'v': 'voiced', 'o': 'op', '+': True, '-': False}
    for user in tmp:
        if user['mode'] in names and user['sign'] in names:
            mode, name, sign = names[user['mode']
                                     ], user['name'], names[user['sign']]
            code.chan[channel][name][mode] = sign
            if mode == 'op' and sign:
                code.chan[channel][name]['voiced'] = True
Пример #11
0
def trigger_250(code, origin, line, args, text):
    """
        ID: RPL_STATSCONN
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #12
0
def trigger_JOIN(code, origin, line, args, text):
    """
        ID:         JOIN
        Decription: The JOIN command is used by client to start listening a specific
                    channel. Whether or not a client is allowed to join a channel is
                    checked only by the server the client is connected to; all other
                    servers automatically add the user to the channel when it is received
                    from other servers.  The conditions which affect this are as follows:
                        1.  the user must be invited if the channel is invite-only;
        Format:     <channel>{,<channel>} [<key>{,<key>}]
    """

    if origin.nick != code.nick:
        code.chan[args[1]][origin.nick] = {
            'normal': True,
            'voiced': False,
            'op': False,
            'count': 0,
            'messages': []
        }
    if not code.debug:
        output.normal('{} has joined {}'.format(origin.nick, args[1]), args[1])
    tmp = {
        'message': 'joined {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'JOIN'
    }
    code.logs['bot'].append(tmp)
    code.write(('WHO', origin.nick, '%tcuhn,1'))
Пример #13
0
def trigger_250(code, origin, line, args, text):
    """
        ID: RPL_STATSCONN
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #14
0
 def initiate_connect(self, host, port):
     count = 0
     max_attempts = 5
     if self.config('connect_delay'):
         delay = int(self.config('connect_delay'))
     else:
         delay = 20
     while True:
         if count >= max_attempts:
             break
         try:
             count += 1
             if count > 1:
                 output.error('Failed to connect! Trying again in '
                              '%s seconds.' % str(delay))
                 time.sleep(delay)
             if self.verbose:
                 output.normal(
                     'Connecting to %s:%s... (try %s)' %
                     (host, port, str(count)), 'STATUS')
             self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
             self.connect((host, port))
             count = 0
             asyncore.loop()
         except:
             pass
     output.error('Too many failed attempts. Exiting.')
     os._exit(1)
Пример #15
0
def trigger_003(code, origin, line, args, text):
    """
        ID:         RPL_CREATED
        Decription: Part of the post-registration greeting. Text varies widely.
        Format:     This server was created <date>
    """

    output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #16
0
def trigger_003(code, origin, line, args, text):
    """
        ID:         RPL_CREATED
        Decription: Part of the post-registration greeting. Text varies widely.
        Format:     This server was created <date>
    """

    output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #17
0
def trigger_001(code, origin, line, args, text):
    """
        ID:         RPL_WELCOME
        Decription: The first message sent after client registration. The text used varies widely.
        Format:     Welcome to the Internet Relay Network <nick>!<user>@<host>
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #18
0
def trigger_001(code, origin, line, args, text):
    """
        ID:         RPL_WELCOME
        Decription: The first message sent after client registration. The text used varies widely.
        Format:     Welcome to the Internet Relay Network <nick>!<user>@<host>
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #19
0
def trigger_KICK(code, line):
    re_tmp = r'^\:(.*?)\!(.*?)\@(.*?) KICK (.*?) (.*?) \:(.*?)$'
    nick, ident, host, sender, kicked, reason = re.compile(re_tmp).match(line).groups()
    output.normal('%s has kicked %s from %s. Reason: %s' % (nick, kicked, sender, reason), 'KICK', 'red')

    # Stuff for user_list
    tmp = line.split('#', 1)[1].split()
    channel, name = '#' + tmp[0], tmp[1]
    del code.chan[channel][name]
Пример #20
0
def trigger_002(code, origin, line, args, text):
    """
        ID:         RPL_YOURHOST
        Decription: Part of the post-registration greeting. Text varies.
                    widely
        Format:     Your host is <servername>, running version <version>
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #21
0
def trigger_002(code, origin, line, args, text):
    """
        ID:         RPL_YOURHOST
        Decription: Part of the post-registration greeting. Text varies.
                    widely
        Format:     Your host is <servername>, running version <version>
    """

    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
Пример #22
0
    def msg(self, recipient, text, x=False, shorten_urls=True, bypass_loop=False, colors=True):
        """
            Sends most messages to a direct location or recipient
            auto shortens URLs by default unless specified in the
            config
        """
        self.sending.acquire()
        if colors:
            text = self.format(text, shorten_urls=shorten_urls)

        if isinstance(text, unicode):
            try:
                text = text.encode('utf-8')
            except UnicodeEncodeError as e:
                text = e.__class__ + ': ' + str(e)
        if isinstance(recipient, unicode):
            try:
                recipient = recipient.encode('utf-8')
            except UnicodeEncodeError as e:
                return

        if not x:
            text = text.replace('\x01', '')

        # No messages within the last 3 seconds? Go ahead!
        # Otherwise, wait so it's been at least 0.5 seconds <nope>+ penalty</nope>
        if not bypass_loop:  # Used if you want to bypass the global rate limiter
            def wait(sk, txt):
                if sk:
                    elapsed = time.time() - sk[-1][0]
                    if elapsed < 3:
                        # penalty = float(max(0, len(txt) - 50)) / 70
                        wait = 0.5  # + penalty
                        if elapsed < wait:
                            time.sleep(wait - elapsed)

            wait(self.stack, text)

            # Loop detection
            messages = [m[1] for m in self.stack[-8:]]
            if messages.count(text) >= 5:
                text = '...'
                if messages.count('...') > 2:
                    self.sending.release()
                    return

        self.__write(('PRIVMSG', self.safe(recipient)), self.safe(text))
        output.normal('(%s) %s' %
                      (self.nick, self.stripcolors(self.clear_format(self.safe(text)))), self.safe(recipient))
        if self.safe(recipient).startswith('#') and self.safe(recipient) in self.logs['channel']:
            self.add_logs(text, recipient, self.nick)
        self.stack.append((time.time(), text))
        self.stack = self.stack[-10:]

        self.sending.release()
Пример #23
0
def trigger_KICK(code, origin, line, args, text):
    """
        ID:         KICK
        Decription: The KICK command can be used to forcibly remove a user from a
                    channel. It 'kicks them out' of the channel (forced PART).
        Format:     <channel> <user> [<comment>]
    """

    output.normal('{} has kicked {} from {}. Reason: {}'.format(
        origin.nick, args[2], args[1], args[3]), 'KICK', 'red')
    del code.chan[args[1]][args[2]]
Пример #24
0
def trigger_JOIN(code, origin, line, args, text):
    if origin.nick != code.nick:
        code.chan[args[1]][origin.nick] = {'normal': True, 'voiced':
                                           False, 'op': False, 'count': 0, 'messages': []}
    output.normal('{} has joined {}'.format(origin.nick, args[1]), args[1])
    tmp = {
        'message': 'joined {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'JOIN'
    }
    code.logs['bot'].append(tmp)
Пример #25
0
Файл: irc.py Проект: adiq/stah
 def initiate_connect(self, host, port):
     output.normal('Connecting to %s:%s... ' %
                   (host, port), 'STATUS')
     try:
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         self.connect((host, port))
         asyncore.loop()
     except KeyboardInterrupt:
         os._exit(0)
     except:
         output.error('Failed to keep connection to %s:%s!' % (host, port))
         sys.exit()
Пример #26
0
def trigger_NOTICE(code, origin, line, args, text):
    if 'Invalid password for ' in text:
        output.error('Invalid NickServ password')
        os._exit(1)
    output.normal('({}) {}'.format(origin.nick, text), 'NOTICE')
    # Add notices to the bot logs
    tmp = {
        'message': text,
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NOTICE'
    }
    code.logs['bot'].append(tmp)
Пример #27
0
def trigger_KICK(code, origin, line, args, text):
    """
        ID:         KICK
        Decription: The KICK command can be used to forcibly remove a user from a
                    channel. It 'kicks them out' of the channel (forced PART).
        Format:     <channel> <user> [<comment>]
    """

    output.normal(
        '{} has kicked {} from {}. Reason: {}'.format(origin.nick, args[2],
                                                      args[1], args[3]),
        'KICK', 'red')
    del code.chan[args[1]][args[2]]
Пример #28
0
 def initiate_connect(self, host, port):
     output.normal('Connecting to %s:%s... ' %
                   (host, port), 'STATUS')
     try:
         # self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         source_address = ((self.config('bind_ip'), 0) if self.config('bind_ip') else None)
         self.set_socket(socket.create_connection((host, port), source_address=source_address))
         self.connect((host, port))
         asyncore.loop()
     except KeyboardInterrupt:
         os._exit(0)
     except Exception as e:
         output.error('Failed to keep connection to %s:%s! (%s)' % (host, port, str(e)))
         sys.exit()
Пример #29
0
def trigger_PRIVMSG(code, origin, line, args, text):
    """
        ID:         PRIVMSG
        Decription: PRIVMSG is used to send private messages between users. <receiver>
                    is the nickname of the receiver of the message. <receiver> can also
                    be a list of names or channels separated with commas.
        Format:     <receiver>{,<receiver>} :<text to be sent>
        Numeric Replies:
           - ERR_NORECIPIENT
           - ERR_NOTEXTTOSEND
           - ERR_CANNOTSENDTOCHAN
           - ERR_NOTOPLEVEL
           - ERR_WILDTOPLEVEL
           - ERR_TOOMANYTARGETS
           - ERR_NOSUCHNICK
           - RPL_AWAY
    """

    text = code.stripcolors(text)
    if text.startswith('\x01ACTION'):
        text = '(me) ' + text.split(' ', 1)[1].strip('\x01')
    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), args[1])

    # Stuff for user_list
    if args[1].startswith('#'):
        if origin.nick not in code.chan[args[1]]:
            code.chan[args[1]][origin.nick] = {'normal': True, 'voiced':
                                               False, 'op': False, 'count': 0, 'messages': []}
        code.chan[args[1]][origin.nick]['count'] += 1
        # 1. per-channel-per-user message storing...
        code.chan[args[1]][origin.nick]['messages'].append(
            {'time': int(time.time()), 'message': text})
        # Ensure it's not more than 20 of the last messages
        code.chan[args[1]][origin.nick]['messages'] = code.chan[
            args[1]][origin.nick]['messages'][-20:]
        # 2. Per channel message storing...
        tmp = {'message': text, 'nick': origin.nick,
               'time': int(time.time()), 'channel': args[1]}
        code.logs['channel'][args[1]].append(tmp)
        code.logs['channel'][args[1]] = code.logs['channel'][args[1]][-20:]
        # 3. All bot messages in/out, maxed out by n * 100 (n being number of
        # channels)
        code.logs['bot'].append(tmp)
        code.logs['bot'] = code.logs['bot'][-(100 * len(code.channels)):]

    for channel in code.chan:
        if origin.nick in code.chan[channel]:
            code.chan[channel][origin.nick]['last_seen'] = int(time.time())
Пример #30
0
def trigger_PRIVMSG(code, line):
    re_tmp = r'^\:(.*?)\!(.*?)\@(.*?) PRIVMSG (.*?) \:(.*?)$'
    nick, ident, host, sender, msg = re.compile(re_tmp).match(line).groups()
    msg = code.stripcolors(msg)
    if msg.startswith('\x01'):
        msg = '(me) ' + msg.split(' ', 1)[1].strip('\x01')
    output.normal('(%s) %s' % (nick, msg), sender)

    # Stuff for user_list
    if sender.startswith('#'):
        if nick not in code.chan[sender]:
            code.chan[sender][nick] = {'normal': True, 'voiced': False, 'op': False, 'count': 0, 'messages': []}
        code.chan[sender][nick]['count'] += 1
        code.chan[sender][nick]['messages'].append({'time': int(time.time()), 'message': msg})
        # Ensure it's not more than 20 of the last messages
        code.chan[sender][nick]['messages'] = code.chan[sender][nick]['messages'][-20:]
Пример #31
0
 def initiate_connect(self, host, port):
     output.normal('Connecting to %s:%s...' % (host, port), 'STATUS')
     try:
         # self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         source_address = ((self.config('bind_ip'),
                            0) if self.config('bind_ip') else None)
         self.set_socket(
             socket.create_connection((host, port),
                                      source_address=source_address))
         self.connect((host, port))
         asyncore.loop()
     except KeyboardInterrupt:
         os._exit(0)
     except Exception as e:
         output.error('Connection to %s:%s failed! (%s)' %
                      (host, port, str(e)))
         os._exit(1)
Пример #32
0
def trigger_NICK(code, origin, line, args, text):
    output.normal('{} is now known as {}'.format(origin.nick, args[1]), 'NICK')

    # Rename old users to new ones in the database...
    for channel in code.chan:
        if origin.nick in code.chan[channel]:
            old = code.chan[channel][origin.nick]
            del code.chan[channel][origin.nick]
            code.chan[channel][args[1]] = old

    tmp = {
        'message': 'is now known as {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'NICK'
    }
    code.logs['bot'].append(tmp)
Пример #33
0
def trigger_PART(code, origin, line, args, text):
    if origin.nick == code.nick:
        del code.chan[args[1]]
        del code.logs['channel'][args[1]]
    else:
        del code.chan[args[1]][origin.nick]
    if len(args) == 3:
        reason = args[2]
    else:
        reason = 'Unknown'
    output.normal('{} has part {}. Reason: {}'.format(
        origin.nick, args[1], reason), args[1])
    tmp = {
        'message': 'left {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'PART'
    }
    code.logs['bot'].append(tmp)
Пример #34
0
def docstring():
    symbol = '*'
    lines = __doc__.strip().split('\n')
    largest = 0
    for line in lines:
        if len(line) > largest:
            largest = len(line)
    outer = (largest + (1 * 4)) * symbol
    output.normal(outer, False)
    for line in lines:
        tmp = symbol + (1 * ' ') + line
        sidedif = (largest + (1 * 4)) - len(tmp) - 1
        tmp += ' ' * sidedif + symbol
        output.normal(tmp, False)
    output.normal(outer, False)
    output.normal('Initializing the bot', 'START')
Пример #35
0
def trigger_JOIN(code, origin, line, args, text):
    """
        ID:         JOIN
        Decription: The JOIN command is used by client to start listening a specific
                    channel. Whether or not a client is allowed to join a channel is
                    checked only by the server the client is connected to; all other
                    servers automatically add the user to the channel when it is received
                    from other servers.  The conditions which affect this are as follows:
                        1.  the user must be invited if the channel is invite-only;
        Format:     <channel>{,<channel>} [<key>{,<key>}]
    """

    if origin.nick != code.nick:
        code.chan[args[1]][origin.nick] = {'normal': True, 'voiced': False, 'op': False, 'count': 0, 'messages': []}
    if not code.debug:
        output.normal('{} has joined {}'.format(origin.nick, args[1]), args[1])
    tmp = {
        'message': 'joined {}'.format(args[1]),
        'nick': origin.nick,
        'time': int(time.time()),
        'channel': 'JOIN'
    }
    code.logs['bot'].append(tmp)
    code.write(('WHO', origin.nick, '%tcuhn,1'))
Пример #36
0
def docstring():
    symbol = '*'
    lines = __doc__.strip().split('\n')
    largest = 0
    for line in lines:
        if len(line) > largest:
            largest = len(line)
    outer = (largest + (1 * 4)) * symbol
    output.normal(outer, False)
    for line in lines:
        tmp = symbol + (1 * ' ') + line
        sidedif = (largest + (1 * 4)) - len(tmp) - 1
        tmp += ' ' * sidedif + symbol
        output.normal(tmp, False)
    output.normal(outer, False)
    output.success('Initializing the bot', 'START')
Пример #37
0
def trigger_write_KICK(code, args, text, raw):
    output.normal('I have kicked {} from {}'.format(args[2], args[1]), 'KICK', 'red')
    if args[2] in code.chan[args[1]]:
        del code.chan[args[1]][args[2]]
Пример #38
0
        if log:
            wait(self.stack_log, text)
        else:
            wait(self.stack, text)

        # Loop detection
        if not log:
            messages = [m[1] for m in self.stack[-8:]]
            if messages.count(text) >= 5:
                text = '...'
                if messages.count('...') > 2:
                    self.sending.release()
                    return

        self.__write(('PRIVMSG', self.safe(recipient)), self.safe(text))
        output.normal('(%s) %s' % (self.nick, self.stripcolors(self.clear_format(self.safe(text)))), self.safe(recipient))
        if log:
            self.stack_log.append((time.time(), text))
        else:
            self.stack.append((time.time(), text))
        self.stack = self.stack[-10:]
        self.stack_log = self.stack_log[-10:]

        self.sending.release()

    def notice(self, dest, text):
        '''Send an IRC NOTICE to a user or a channel. See IRC protocol
           documentation for more information'''
        text = self.format(text)
        self.write(('NOTICE', dest), text)
Пример #39
0
def trigger_MODE(code, origin, line, args, text):
    """
        ID:         MODE
        Decription: The MODE command is a dual-purpose command in IRC. It allows both
                    usernames and channels to have their mode changed. The rationale for
                    this choice is that one day nicknames will be obsolete and the
                    equivalent property will be the channel.

                    When parsing MODE messages, it is recommended that the entire message
                    be parsed first and then the changes which resulted then passed on.
        Format:     <channel> {[+|-]|o|p|s|i|t|n|b|v} [<limit>] [<user>] [<ban mask>]
                    <nickname> {[+|-]|i|w|s|o}
    """
    if len(args) == 4:
        if args[1].startswith('#') and '+b' in args:
            code.bans[args[1]] = []
            code.write(['MODE', args[1], '+b'])

    if len(args) == 3:
        if not code.debug:
            output.normal('{} sets MODE {}'.format(origin.nick, text), 'MODE')
        return
    else:
        if not code.debug:
            output.normal('{} sets MODE {}'.format(origin.nick, args[2]), args[1])

    # Stuff for user_list
    data = ' '.join(args[1:])

    channel, modes, users = data.strip().split(' ', 2)
    users = users.split()
    tmp = []

    def remove(old, sign):
        tmp = []
        modes = []
        for char in old:
            modes.append(char)
        while sign in modes:
            i = modes.index(sign)
            tmp.append(i)
            del modes[i]
        return tmp, ''.join(modes)

    if modes.startswith('+'):
        _plus, new_modes = remove(modes, '+')
        _minus, new_modes = remove(new_modes, '-')
    else:
        _minus, new_modes = remove(modes, '-')
        _plus, new_modes = remove(new_modes, '+')

    for index in range(len(users)):
        _usr = users[index]
        _mode = new_modes[index]
        _sign = ''
        if index in _plus:
            _sign = '+'
        if index in _minus:
            _sign = '-'
        tmp.append({'name': _usr, 'mode': _mode, 'sign': _sign})

    last_used = ''

    for index in range(len(tmp)):
        if not last_used:
            last_used = tmp[index]['sign']
        if not tmp[index]['sign'] or len(tmp[index]['sign']) == 0:
            tmp[index]['sign'] = last_used
        else:
            last_used = tmp[index]['sign']

    names = {'v': 'voiced', 'o': 'op', '+': True, '-': False}
    for user in tmp:
        if user['mode'] in names and user['sign'] in names:
            mode, name, sign = names[user['mode']], user['name'], names[user['sign']]
            code.chan[channel][name][mode] = sign
            if mode == 'op' and sign:
                code.chan[channel][name]['voiced'] = True
Пример #40
0
def trigger_PRIVMSG(code, origin, line, args, text):
    """
        ID:         PRIVMSG
        Decription: PRIVMSG is used to send private messages between users. <receiver>
                    is the nickname of the receiver of the message. <receiver> can also
                    be a list of names or channels separated with commas.
        Format:     <receiver>{,<receiver>} :<text to be sent>
        Numeric Replies:
           - ERR_NORECIPIENT
           - ERR_NOTEXTTOSEND
           - ERR_CANNOTSENDTOCHAN
           - ERR_NOTOPLEVEL
           - ERR_WILDTOPLEVEL
           - ERR_TOOMANYTARGETS
           - ERR_NOSUCHNICK
           - RPL_AWAY
    """

    text = code.stripcolors(text)
    if text.startswith('\x01ACTION'):
        text = '(me) ' + text.split(' ', 1)[1].strip('\x01')
    if not code.debug:
        output.normal('({}) {}'.format(origin.nick, text), args[1])

    # Stuff for user_list
    if args[1].startswith('#'):
        if origin.nick not in code.chan[args[1]]:
            code.chan[args[1]][origin.nick] = {
                'normal': True,
                'voiced': False,
                'op': False,
                'count': 0,
                'messages': []
            }
        code.chan[args[1]][origin.nick]['count'] += 1
        # 1. per-channel-per-user message storing...
        code.chan[args[1]][origin.nick]['messages'].append({
            'time':
            int(time.time()),
            'message':
            text
        })
        # Ensure it's not more than 20 of the last messages
        code.chan[args[1]][origin.nick]['messages'] = code.chan[args[1]][
            origin.nick]['messages'][-20:]
        # 2. Per channel message storing...
        tmp = {
            'message': text,
            'nick': origin.nick,
            'time': int(time.time()),
            'channel': args[1]
        }
        code.logs['channel'][args[1]].append(tmp)
        code.logs['channel'][args[1]] = code.logs['channel'][args[1]][-20:]
        # 3. All bot messages in/out, maxed out by n * 100 (n being number of
        # channels)
        code.logs['bot'].append(tmp)
        code.logs['bot'] = code.logs['bot'][-(100 * len(code.channels)):]

    for channel in code.chan:
        if origin.nick in code.chan[channel]:
            code.chan[channel][origin.nick]['last_seen'] = int(time.time())
Пример #41
0
def trigger_MODE(code, origin, line, args, text):
    """
        ID:         MODE
        Decription: The MODE command is a dual-purpose command in IRC. It allows both
                    usernames and channels to have their mode changed. The rationale for
                    this choice is that one day nicknames will be obsolete and the
                    equivalent property will be the channel.

                    When parsing MODE messages, it is recommended that the entire message
                    be parsed first and then the changes which resulted then passed on.
        Format:     <channel> {[+|-]|o|p|s|i|t|n|b|v} [<limit>] [<user>] [<ban mask>]
                    <nickname> {[+|-]|i|w|s|o}
    """
    if len(args) == 4:
        if args[1].startswith('#') and '+b' in args:
            code.bans[args[1]] = []
            code.write(['MODE', args[1], '+b'])

    if len(args) == 3:
        if not code.debug:
            output.normal('{} sets MODE {}'.format(origin.nick, text), 'MODE')
        return
    else:
        if not code.debug:
            output.normal('{} sets MODE {}'.format(origin.nick, args[2]),
                          args[1])

    # Stuff for user_list
    data = ' '.join(args[1:])

    channel, modes, users = data.strip().split(' ', 2)
    users = users.split()
    tmp = []

    def remove(old, sign):
        tmp = []
        modes = []
        for char in old:
            modes.append(char)
        while sign in modes:
            i = modes.index(sign)
            tmp.append(i)
            del modes[i]
        return tmp, ''.join(modes)

    if modes.startswith('+'):
        _plus, new_modes = remove(modes, '+')
        _minus, new_modes = remove(new_modes, '-')
    else:
        _minus, new_modes = remove(modes, '-')
        _plus, new_modes = remove(new_modes, '+')

    for index in range(len(users)):
        _usr = users[index]
        _mode = new_modes[index]
        _sign = ''
        if index in _plus:
            _sign = '+'
        if index in _minus:
            _sign = '-'
        tmp.append({'name': _usr, 'mode': _mode, 'sign': _sign})

    last_used = ''

    for index in range(len(tmp)):
        if not last_used:
            last_used = tmp[index]['sign']
        if not tmp[index]['sign'] or len(tmp[index]['sign']) == 0:
            tmp[index]['sign'] = last_used
        else:
            last_used = tmp[index]['sign']

    names = {'v': 'voiced', 'o': 'op', '+': True, '-': False}
    for user in tmp:
        if user['mode'] in names and user['sign'] in names:
            mode, name, sign = names[user['mode']], user['name'], names[
                user['sign']]
            code.chan[channel][name][mode] = sign
            if mode == 'op' and sign:
                code.chan[channel][name]['voiced'] = True
Пример #42
0
def trigger_NOTICE(code, line):
    re_tmp = r'^\:(.*?) NOTICE (.*?) \:(.*?)$'
    nick, sender, msg = re.compile(re_tmp).match(line).groups()
    output.normal('(%s) %s' % (nick.split('!')[0], msg), 'NOTICE')
Пример #43
0
def trigger_NICK(code, line):
    nick = line[1::].split('!', 1)[0]
    new_nick = line[1::].split(':', 1)[1]
    output.normal('%s is now known as %s' % (nick, new_nick), 'NICK')
Пример #44
0
def trigger_250(code, line):
    msg, sender = line.split(':', 2)[2], line.split(':', 2)[1].split()[0]
    output.normal('(%s) %s' % (sender, msg), 'NOTICE')
Пример #45
0
def trigger_write_KICK(code, args, text, raw):
    output.normal('I have kicked {} from {}'.format(args[2], args[1]), 'KICK',
                  'red')
    if args[2] in code.chan[args[1]]:
        del code.chan[args[1]][args[2]]
Пример #46
0
                    if elapsed < wait:
                        time.sleep(wait - elapsed)

        wait(self.stack, text)

        # Loop detection
        messages = [m[1] for m in self.stack[-8:]]
        if messages.count(text) >= 5:
            text = '...'
            if messages.count('...') > 2:
                self.sending.release()
                return

        self.__write(('PRIVMSG', self.safe(recipient)), self.safe(text))
        output.normal(
            '(%s) %s' %
            (self.nick, self.stripcolors(self.clear_format(self.safe(text)))),
            self.safe(recipient))
        self.stack.append((time.time(), text))
        self.stack = self.stack[-10:]

        self.sending.release()

    def notice(self, dest, text):
        '''Send an IRC NOTICE to a user or a channel. See IRC protocol
           documentation for more information'''
        text = self.format(text)
        self.write(('NOTICE', dest), text)

    def action(self, dest, text):
        '''Send an action (/me) to a user or a channel'''
        text = self.format(text)
Пример #47
0
    def msg(self,
            recipient,
            text,
            x=False,
            shorten_urls=True,
            bypass_loop=False,
            colors=True):
        """
            Sends most messages to a direct location or recipient
            auto shortens URLs by default unless specified in the
            config
        """
        self.sending.acquire()
        if colors:
            text = self.format(text, shorten_urls=shorten_urls)

        if isinstance(text, unicode):
            try:
                text = text.encode('utf-8')
            except UnicodeEncodeError as e:
                text = e.__class__ + ': ' + str(e)
        if isinstance(recipient, unicode):
            try:
                recipient = recipient.encode('utf-8')
            except UnicodeEncodeError as e:
                return

        if not x:
            text = text.replace('\x01', '')

        # No messages within the last 3 seconds? Go ahead!
        # Otherwise, wait so it's been at least 0.5 seconds <nope>+ penalty</nope>
        if not bypass_loop:  # Used if you want to bypass the global rate limiter

            def wait(sk, txt):
                if sk:
                    elapsed = time.time() - sk[-1][0]
                    if elapsed < 3:
                        # penalty = float(max(0, len(txt) - 50)) / 70
                        wait = 0.5  # + penalty
                        if elapsed < wait:
                            time.sleep(wait - elapsed)

            wait(self.stack, text)

            # Loop detection
            messages = [m[1] for m in self.stack[-8:]]
            if messages.count(text) >= 5:
                text = '...'
                if messages.count('...') > 2:
                    self.sending.release()
                    return

        self.__write(('PRIVMSG', self.safe(recipient)), self.safe(text))
        output.normal(
            '(%s) %s' %
            (self.nick, self.stripcolors(self.clear_format(self.safe(text)))),
            self.safe(recipient))
        if self.safe(recipient).startswith('#') and self.safe(
                recipient) in self.logs['channel']:
            self.add_logs(text, recipient, self.nick)
        self.stack.append((time.time(), text))
        self.stack = self.stack[-10:]

        self.sending.release()