Exemplo n.º 1
0
def h_tell_sent(bot, id, target, sent_msgs, reply_msg=None):
    if reply_msg is None:
        reply_msg = 'It shall be done'
    if len(sent_msgs) > 1: reply(bot, id, target,
        '%s (%s messages sent).' % (reply_msg, len(sent_msgs)))
    else: reply(bot, id, target,
        '%s (1 message sent to "%s").' % (reply_msg, sent_msgs[0].to_nick))
Exemplo n.º 2
0
def h_xray(bot, id, chan, args, full_msg):
    if chan.lower() not in games: return
    game = games[chan.lower()]
    if chan.lower() != game.names[game.player].lower(): reply(bot, id, chan,
        'Error: it is not your turn.'); return

    args = args.split()
    if args:
        value = args[0]
        if value in '123': value = int(value)
        else: reply(bot, id, chan,
            'Error: "%s" is not a valid 3-sided die value.' % value); return
    else:
        player, opponent = game.player, other_colour(game.player)
        values = [dv for (dc,dv) in game.dice[player] if dc == opponent]
        if not values: reply(bot, id, chan,
            'Error: %s does not possess any %s dice.'
            % (player, opponent.lower())); return
        if not all(v == values[0] for v in values[1:]): reply(bot, id, chan,
            'Error: %s has %s dice with different values; you must specify'
            ' the value to sacrifice.' % (player, opponent.lower())); return
        value = values[0]

    try:
        game.xray(value)
    except IllegalMove as exc:
        reply(bot, id, chan, 'Error: %s' % exc)
        return
    yield util.sub(end_move(bot, game))
Exemplo n.º 3
0
def h_help_seen(bot, reply, args):
    reply('seen NICK\2 or \2!seen NICK!USER@HOST',
    'Tells how long ago any user was seen in the channel with the given NICK,'
    ' or matching the given hostmask. Both forms may contain the wildcard'
    ' characters * and ?, which stand for zero or more characters and exactly'
    ' one character, respectively. Examples: "!seen Alice",'
    ' "!seen *@*.alicedsl.se".')
Exemplo n.º 4
0
def h_dismiss(bot, id, chan, query, full_msg, reply):
    if chan is None: return reply(
        'Error: the !dismiss command may only be used in a channel.')

    state = get_state()
    msgs = [m for m in state.msgs
            if m.channel.lower() == chan.lower()
            and (not query or match_id(query, m.from_id))]

    msgs = [m for m in state.msgs if would_deliver(id, chan, m)
            and (not query or match_id(query, m.from_id))]
    if not msgs: return reply(
        'You have no messages%s to dismiss.' % (query and ' from "%s"' % query))

    msg = msgs[-1]
    state.msgs.remove(msg)
    state.dismissed_msgs = [m for m in state.dismissed_msgs
        if (datetime.datetime.utcnow() - m.time_sent).days <= DISMISS_DAYS]
    state.dismissed_msgs.append(msg)

    count = len([m for m in state.msgs if would_deliver(id, chan, m)])
    msg = ('1 message from %s deleted; you now have %s message%s'
       ' (you may reverse this using !undismiss).'
       % (msg.from_id.nick, count, 's' if count != 1 else ''))

    put_state(state)
    reply(msg)
Exemplo n.º 5
0
def h_tell_undo(bot, id, target, args, full_msg):
    try:
        redo_state()
    except HistoryEmpty:
        reply(bot, id, target, 'Error: no redo state is available.')
    else:
        reply(bot, id, target, 'Done.')
Exemplo n.º 6
0
def h_help_undismiss(bot, reply, args):
    reply('undismiss\2 or \2!undismiss NICK',
    'Reverses the effect of \2!dismiss\2, restoring the last dismissed message'
    ' from NICK, or from anybody if NICK is not specified. This may be done'
    ' multiple times to restore messages from up to %s days ago. As with'
    ' \2!dismiss\2, NICK may take the form NICK!USER@HOST, and may contain the'
    ' wildcard characters * and ?, and alternatives separated by /.' % DISMISS_DAYS)
Exemplo n.º 7
0
def h_url_collect_urls(bot, urls, chan, id, orig_msg):
    if not chan: return
    chan = chan.lower()
    delete_indices = []

    for url_spec, index in izip(urls, count()):
        url, is_nsfw = url_collect.url_nsfw(url_spec)
        for tries in xrange(UPLOAD_RETRIES):
            if tries: yield runtime.sleep(UPLOAD_RETRY_S)
            mirror_url = get_mirror_url(url, chan)
            if mirror_url: break
        else:
            continue

        nsfw_str = '\2NSFW:\2 ' if is_nsfw else ''
        msg = '%s%s copied to <%s>.' % (nsfw_str, url, mirror_url)
        message.reply(bot, id, chan, msg, prefix=False)
        url_collect.history[chan].append([
            url_collect.nsfw_url(mirror_url, is_nsfw)])
        delete_indices.append(index)

    new_urls = [u for (i,u) in izip(count(),urls) if i not in delete_indices]
    for index in xrange(len(url_collect.history[chan])-1, -1, -1):
        if url_collect.history[chan][index] == urls:
            if new_urls:
                url_collect.history[chan][index] = new_urls
            else:
                del url_collect.history[chan][index]
            break
Exemplo n.º 8
0
def h_message(bot, id, target, msg):
    now = time.time()
    if target and target in last and now < last[target] + PERIOD:
        return
    if re.search("(h[eau]+){2,}", msg, re.I):
        last[target] = now        
        reply(bot, id, target, laugh(), prefix=False)
Exemplo n.º 9
0
def untell_nicks(bot, id, target, channel, args):
    state = get_state()
    count = dict()

    def would_cancel(msg, to_nick):
        if msg.channel.lower() != channel.lower(): return False
        if msg.to_nick.lower() != to_nick.lower(): return False
        if msg.from_id != id: return False
        return True

    for to_nick in [n.strip() for n in args.split(',')]:
        msgs = [(would_cancel(m, to_nick), m) for m in state.msgs]
        msgs_cancel = [m for (b, m) in msgs if b]
        msgs_keep = [m for (b, m) in msgs if not b]
        count[to_nick] = len(msgs_cancel)
        if len(msgs_cancel): state.msgs = msgs_keep

    total = sum(count.itervalues())
    msg = '%s message%s deleted.' % (total, 's' if total != 1 else '')

    empty = ['"%s"' % nick for (nick, count) in count.iteritems() if not count]
    if empty:
        list = ', '.join(empty[:-2] + [' or '.join(empty[-2:])])
        msg += (' There were no messages to %s, from %s in %s.'
            % (list, '%s!%s@%s' % tuple(id), channel))

    put_state(state)
    reply(bot, id, target, msg)
Exemplo n.º 10
0
def h_urk(bot, id, target, args, full_msg):
    reply_url = random.choice((
        'https://i.imgur.com/j9wHs6W.jpg',
        'https://i.imgur.com/oHbphbE.jpg',
        'https://i.imgur.com/dUKjGVi.jpg',
        'https://i.imgur.com/jKLxKdY.jpg',
    ))
    message.reply(bot, id, target, '%s Urk!' % reply_url, prefix=False)
Exemplo n.º 11
0
def h_help_upoopia(bot, reply, *args):
    reply('upoopia #CHANNEL [b[lue]|r[ed]]',
    '    Challenge #CHANNEL to a game of Upoopia:'
    ' <http://www.unicorn7.org/games/game/553/>. To play on IRC, each player'
    ' must be in a separate channel with operator status, then each player'
    ' must send a challenge to the other channel, optionally indicating their'
    ' preferred colour, where, by convention, Blue moves first.')
    reply('upoopia',
    '    With no arguments, start a new game with the last channel played.')
Exemplo n.º 12
0
def h_help_convert(bot, reply, args):
    reply('convert QUANTITY [to] UNIT')
    reply('cv QUANTITY [to] UNIT',
    'Convert a quantity expressed in a certain unit to another unit of'
    ' measurement. Currently, the following units are supported: metre, inch,'
    ' foot, yard, mile, nautical mile, league, Planck length; gram, ounce,'
    ' pound, stone, Planck mass; degree Celsius, Farenheit, Kelvin, Rankine.'
    ' SI prefixes may be used with suitable metric units. Standard'
    ' abbreviations may be used.')
Exemplo n.º 13
0
def h_resign(bot, id, chan, args, full_msg):
    if chan.lower() not in games: return
    game = games[chan.lower()]
    if chan.lower() != game.names[game.player].lower(): reply(bot, id, chan,
        'Error: it is not your turn.'); return
    try:
        game.resign()
    except IllegalMove as exc:
        reply(bot, id, chan, 'Error: %s' % exc)
        return
    yield util.sub(end_move(bot, game))
Exemplo n.º 14
0
def h_message_ignored(bot, id, target, msg):
    if not target:
        return
    if random.randrange(IFREQ) != 0:
        return
    if len(msg) > MAXLEN or len(msg) < MINLEN:
        return
    msg = bum_replace(msg)
    if not msg:
        return
    reply(bot, id, target, msg, prefix=False)
Exemplo n.º 15
0
 def admin_decd(*args, **kwds):
     cargs = inspect.getcallargs(func, *args, **kwds)
     bot, id = cargs['bot'], cargs['id']
     is_admin = yield check(bot, id)
     if is_admin:
         token = object()
         bot.link(token, func)
         bot.drive(token, *args, **kwds)
         bot.unlink(token, func)
     elif 'target' in cargs or 'chan' in cargs:
         target = cargs.get('target') or cargs.get('chan')
         reply(bot, id, cargs['target'], 'Access denied.')
Exemplo n.º 16
0
def h_missed_rolls(bot, id, target, args, full_msg):
    if target is None:
        message.reply(bot, id, target,
            'Error: this command may not be used by PM; however, see'
            ' \2!help view-missed-rolls\2 for a version which can.')
    elif args:
        message.reply(bot, id, target,
            'Error: this command does not accept any parameters. Perhaps you'
            ' meant \2!view-missed-rolls\2.')
    else:
        def reply(msg):
            message.reply(bot, id, target, msg, prefix=False, wrap=True)
        yield show_missed_rolls(bot, target, reply, delete=True)
Exemplo n.º 17
0
def h_read(bot, id, chan, args, full_msg):
    if chan is not None:
        msgs = deliver_msgs(bot, id, chan, explicit=True)
        if not msgs:
            reply(bot, id, chan, 'You have no messages.')
        return

    state = get_state()
    all_msgs = [m for m in state.msgs if would_deliver(id, None, m)
                and m in state.last_notify]

    earliest = dict()
    for msg in all_msgs:
        earliest[msg.channel.lower()] = min(
            msg.time_sent, earliest.get(msg.channel.lower(), msg.time_sent))
    all_msgs = sorted(all_msgs, key=lambda m: earliest[m.channel.lower()])

    if not all_msgs:
        reply(bot, id, chan, 'No messages are available to read.')
        return

    msgs = all_msgs[:MAX_DELIVER_PM]
    remain_msgs = all_msgs[MAX_DELIVER_PM:]
    while 1 <= sum(1 for m in remain_msgs if m.channel == msgs[-1].channel) \
            <= MAX_DELIVER_CHAN:
        msgs.append(remain_msgs.pop(0))

    for msg, index in izip(msgs, count(1)):
        tag = '%d/%d: ' % (index, len(all_msgs))
        deliver_msg(bot, id, None, msg, tag=tag)
        yield runtime.sleep(0)

    state = get_state()
    state.msgs = [m for m in state.msgs if m not in msgs]
    put_state(state)

    next_msgs = remain_msgs[:MAX_DELIVER_PM]
    next_remain_msgs = remain_msgs[MAX_DELIVER_PM:]
    while any(n <= MAX_DELIVER_CHAN
    for n in Counter(m.channel for m in next_remain_msgs).itervalues()):
        next_msgs.append(next_remain_msgs.pop(0))

    noun = 'message' if len(remain_msgs) == 1 else 'messages'
    if next_remain_msgs: reply(bot, id, chan,
        'Say \2!read\2 to read the next %d of %d %s.' % (
        len(next_msgs), len(remain_msgs), noun))
    elif next_msgs: reply(bot, id, chan,
        'Say \2!read\2 to read the remaining %d %s.' % (
        len(next_msgs), noun))
    else: reply(bot, id, chan,
        'End of messages.')
Exemplo n.º 18
0
def untell_last(bot, id, target, channel):
    state = get_state()
    def would_cancel(msg):
        if msg.channel.lower() != channel.lower(): return False
        if msg.from_id != id: return False
        return True
    cancel_msgs = filter(would_cancel, state.msgs)

    if cancel_msgs:
        last_msg = cancel_msgs[-1]
        state.msgs = [m for m in state.msgs if m is not last_msg]
        put_state(state)
        msg = '1 message to "%s" deleted.' % last_msg.to_nick
    else:
        msg = 'Error: you have no messages to cancel.'
    reply(bot, id, target, msg)
Exemplo n.º 19
0
def h_cancel(bot, id, chan, args, full_msg):
    # Cancel any existing challenge.
    if chan.lower() in challenges:
        opp_chan, colour = challenges.pop(chan.lower())
        reply(bot, id, chan,
            'Challenge to %s cancelled.' % opp_chan, prefix=False)
        yield util.sub(end_session(bot, chan))
    # Cancel any existing game.
    if chan.lower() in games:
        game = games[chan.lower()]
        [opp_chan] = [c for c in game.names.values() if c.lower()!=chan.lower()]
        for game_chan in game.names.itervalues():
            del games[game_chan.lower()]
            bot.send_msg(game_chan,
                'The game of Upoopia has been cancelled.', no_link=True)
        yield util.sub(end_session(bot, chan, opp_chan))
Exemplo n.º 20
0
def h_tell_remove(bot, id, target, args, full_msg):
    state = get_state()
    msgs = state.msgs
    if target:
        msgs = filter(lambda m: m.channel.lower() == target.lower(), msgs)
    remove_msgs = []
    try:
        for match in re.finditer(r'\S+', args):
            index = int(match.group()) - 1
            remove_msgs.append(msgs[index])
        for msg in remove_msgs:
            state.msgs.remove(msg)
    except Exception as e:
        return reply(bot, id, target, repr(e))
    put_state(state)
    reply(bot, id, target, 'Done.')
Exemplo n.º 21
0
def h_untell(bot, id, target, args, full_msg):
    # Secretly, admins may prepend the arguments with the target channel.
    match = re.match(r'(#\S+)\s+(.*)', args)
    if match:
        is_admin = yield auth.check(bot, id)
        if is_admin: channel, args = match.groups()
    elif target:
        channel = target
    else:
        reply(bot, id, target,
            'Error: the !untell command may only be used in a channel.')
        return # No return with argument allowed in a generator.

    if args:
        untell_nicks(bot, id, target, channel, args)
    else:
        untell_last(bot, id, target, channel)
Exemplo n.º 22
0
def deliver_msg(bot, id, chan, msg, tag=''):
    delta = datetime.datetime.utcnow() - msg.time_sent
    if delta.total_seconds() < 1: return False
    d_mins, d_secs = divmod(delta.seconds, 60)
    d_hours, d_mins = divmod(d_mins, 60)

    reply(bot, id, chan, '%s%s said%s on %s UTC (%s ago):' % (
        tag,
        '%s!%s@%s' % tuple(msg.from_id),
        (' in %s' % msg.channel) if chan is None else '',
        msg.time_sent.strftime('%d %b %Y, %H:%M'),
        '%sd, %02d:%02d:%02d' % (delta.days, d_hours, d_mins, d_secs)))
    reply(bot, id, chan, "<%s> %s" % (msg.from_id.nick, msg.message),
        prefix=False)

    bot.drive('TELL_DELIVERY', bot, msg.from_id, id, chan, msg.message)
    return True
Exemplo n.º 23
0
def h_help_dismiss(bot, reply, args):
    if args and int(args) == 2:
        reply('dismiss [NICK] [!dismiss [NICK] ...]',
        'If NICK is given, dismisses the most recent message left for you by NICK,'
        ' preventing it from being delivered; otherwise, dismisses the most recent'
        ' message left by anybody. Messages may be recovered using \2!undismiss\2.'
        ' Up to 3 additional !dismiss commands may be given on the same line.',
        'NICK may be an IRC nick or a NICK!USER@HOST, may contain the wildcard'
        ' characters * and ?, and may contain alternatives separated by /, as'
        ' specified in \2!help tell 2\2; in which case, the most recent matching'
        ' message is dismissed.')
    else:
        reply('dismiss\2 or \2!dismiss NICK',
        'Dismisses without showing it the most recent message left for you via'
        ' the \2!tell\2 command. If NICK is given, dismisses the most recent'
        ' message left by that nick. For advanced features, see'
        ' \2!help dismiss 2\2. See also: \2!help undismiss\2.')
Exemplo n.º 24
0
def h_dom_query(bot, id, chan, args, full_msg, cont):
    try:
        if chan is None: return
        cobj = state.channels.get(chan.lower())
        urls = cobj.games if cobj else []
        if urls:
            for index, url in izip(count(), urls):
                name = getattr(state.games[url], 'name', None) \
                       if url in state.games else None
                message.reply(bot, id, chan, '%d. %s%s%s' % (
                    index + 1,
                    url,
                    ' (%s)' % name if name is not None else '',
                    ',' if index < len(urls)-1 else '.'), prefix=False)
        else:
            message.reply(bot, id, chan, 'None.', prefix=False)
    finally:
        yield cont
Exemplo n.º 25
0
def notify_msgs(bot, id, chan):
    state = get_state()
    msgs = filter(lambda m: would_deliver(id, chan, m), state.msgs)
    if not msgs:
        return

    noun, pronoun = ('messages', 'them') if len(msgs) > 1 else ('message', 'it')
    if len(msgs) <= MAX_DELIVER_CHAN:
        reply(bot, id, chan,
            'You have %s %s; say anything to read %s.' % (
            len(msgs), noun, pronoun))
    else:
        reply(bot, id, chan,
            'You have %s %s; use "\2/msg %s !read\2" to read %s.' % (
            len(msgs), noun, bot.nick, pronoun))

    for msg in msgs:
        set_last_notify(msg, state)
    set_state(state)
Exemplo n.º 26
0
def h_tell_add(bot, id, target, args, full_msg):
    args = [a.strip() for a in args.split(',', 4)]
    if len(args) != 5: return reply(bot, id, target,
        'Error: expected: FROM_ID, TO_NICK, CHAN, %s, MESSAGE...'
         % DATE_FORMAT_SHORT)

    [from_id, to_nick, channel, time_sent, message] = args
    try:
        from_id = util.ID(*re.match(r'(.*?)!(.*?)@(.*)$', from_id).groups())
        time_sent = datetime.datetime.strptime(time_sent, DATE_FORMAT_SHORT)
    except Exception as e: return reply(bot, id, target, repr(e))

    msg = Message(from_id=from_id, to_nick=to_nick, channel=channel,
                  time_sent=time_sent, message=message)
    state = get_state()
    state.msgs.append(msg)
    state.msgs.sort(key=lambda m: m.time_sent)
    put_state(state)
    reply(bot, id, target, 'Done.')
Exemplo n.º 27
0
def h_help_tell(bot, reply, args):
    if args and int(args) == 2:
        reply('tell NICK MESSAGE\2 or \2tell NICK[, NICK[, ...]]: MESSAGE',
        'Leaves a message for the given NICK, or for each of the listed NICKs,'
        ' so that it will be delivered to them when next seen in this channel.'
        ' \2!page\2 may be used as a synonym for \2!tell\2.',
        'If NICK contains any occurrence of ! or @, it will be matched against'
        ' the full NICK!USER@HOST of the recipient instead of just their nick.'
        ' If NICK contains the wildcard characters * or ?, these will match any'
        ' sequence of 0 or more characters, or exactly 1 character,'
        ' respectively.',
        'Additionally, NICK may consist of multiple alternatives separated by'
        ' the forward slash character (/), in which case the message will be'
        ' delivered to the first of these recipients that is seen.')
    else:
        reply('tell NICK MESSAGE',
        'Leaves a message for NICK so that it will be delivered to them when'
        ' next seen in this channel. Example: "!tell alice Hello.". For'
        ' advanced features, see \2!help tell 2\2. See also: \2!help untell\2'
        ' and \2!help dismiss\2.')
Exemplo n.º 28
0
def h_view_missed_rolls(bot, id, target, args, full_msg):
    if args:
        nicks = map(str.lower, channel.track_channels[args.lower()])
        if id.nick.lower() not in nicks:
            message.reply(bot, id, target,
                'Error: you must be in "%s" to view its rolls.' % args)
            return
        chan = args
        local = False
    elif not target:
        message.reply(bot, id, target,
            'Error: you must specify a channel. See'
            ' \2!help view-missed-rolls\2 for correct usage.')
        return
    else:
        chan = target
        local = True

    def reply(msg):
        message.reply(bot, id, target, msg, prefix=False, wrap=True)
    yield show_missed_rolls(bot, chan, reply, delete=False, local=local)
Exemplo n.º 29
0
def h_dom_add(bot, id, chan, add_spec, full_msg, cont):
    try:
        if chan is None: return
        chan = chan.lower()
        aurls = re.findall(r'\S+', add_spec.lower())
        if chan in state.channels:
            cobj = state.channels[chan]
        else:
            cobj = state.channels[chan] = Channel()
        for aurl in aurls:
            if aurl in cobj.games:
                message.reply(bot, id, chan,
                    'Error: "%s" is already monitored here.' % aurl)
                break
        else:
            for aurl in aurls:
                cobj.games.append(aurl)

            try:
                state.save_path(STATE_FILE)
            except Exception as e:
                message.reply(bot, id, chan, 'Error: %s' % str(e))
                raise
        
            message.reply(bot, id, chan, '%d game(s) added.' % len(aurls))
            yield update_urls(bot, aurls, None)
    finally:
        yield cont
Exemplo n.º 30
0
def h_undismiss(bot, id, chan, query, *args):
    if chan == None: return reply(bot, id, chan,
        'Error: the !undismiss command may only be used in a channel.')

    state = get_state()
    msgs = [m for m in state.dismissed_msgs if would_deliver(id, chan, m)
            and (not query or match_id(query, m.from_id))]
    if not msgs: return reply(bot, id, chan,
        'You have no dismissed messages%s.'
        % (query and ' from "%s"' % query))
    msg = msgs[-1]
    state.dismissed_msgs.remove(msg)
    state.msgs.append(msg)

    count = len([m for m in state.msgs if would_deliver(id, chan, m)])
    msg = ('1 message from %s restored; you now have %s message%s'
        ' (say anything to read %s).'
        % (msg.from_id.nick, count, 's' if count != 1 else '',
        'them' if count != 1 else 'it'))

    put_state(state)
    reply(bot, id, chan, msg)