Beispiel #1
0
def _handle_command(client, channel, nick, message, cmd, args):
    topic = args[1] if len(args) > 1 else _DEFAULT_TOPIC
    kwargs = {}
    if args[0] == 'ingest' or args[0] == 'learn':
        learning_type = args[2] if len(args) > 2 else ''
        learning_type_source = args[3] if len(args) > 3 else ''
        text = ''
        if learning_type == 'text':
            text = learning_type_source
        elif learning_type == 'url':
            text = requests.get(learning_type_source).text
        elif learning_type == 'dpaste':
            soup = BeautifulSoup(
                requests.get(learning_type_source).text, "html.parser")
            text = soup.select('.highlight')[0].text
        elif learning_type == 'twitter':
            twitter_kwargs = {}
            topic_tweet = db.markovify.find_one({'topic': topic})
            if topic_tweet:
                twitter_kwargs['since_id'] = topic_tweet['since_id']
            try:
                tweets, since_id = twitter_timeline(learning_type_source,
                                                    **twitter_kwargs)
                for tweet in tweets:
                    text = punctuate(text, tweet, _ADD_PUNCTUATION)
                kwargs['since_id'] = since_id
            except Exception as e:
                return 'Error ingesting topic: ' + topic + ' error: ' + str(e)
        elif learning_type == 'logs':
            # would be wise to save the last accessed date, but we also save on
            # chat. since you can drop corpus, let's avoid this for now.
            _, logs = parse_logs(args[3:], channel=learning_type_source)
            regex = re.compile(r'\n[0-9][0-9]:[0-9][0-9]:[0-9][0-9] - \w+ - ',
                               re.I)
            text = re.sub(regex, '. ', logs)
        try:
            ingest(topic, text, **kwargs)
            return random_ack()
        except ValueError as e:
            return str(e)
    elif args[0] == 'generate':
        try:
            return generate(topic, _ADD_PUNCTUATION, **kwargs)
        except Exception as e:
            return str(e)
    elif args[0] == 'drop' or args[0] == 'delete':
        db.markovify.delete_many({'topic': topic})
        return random_ack()
    return "I don't understand args %s" % str(args)
Beispiel #2
0
def _quip_manage(client, channel, nick, message, args):
    """ Add/remove quip/phrase to stash """
    if args[0] == 'drop':
        db.helga_quip.entries.drop()
    elif args[0] == 'dump':
        quips = [
            p['regex'] + ' | ' + p['kind'] + ' | ' + pretty_map(p['options'])
            for p in db.helga_quip.entries.find()
        ]
        if not quips:
            return "Quip database empty"
        payload = {'title': 'helga-quip dump', 'content': '\n'.join(quips)}
        r = requests.post("http://dpaste.com/api/v2/", payload)
        return r.headers['location']
    else:
        valid_options = ['-q']

        # Filter out any option that is invalid and turn them into a
        # dict
        options = dict(
            map(lambda o: (o[:2], o[2:]),
                filter(lambda o: o[:2] in valid_options, args[3:])))

        phrase = {'kind': args[1], 'regex': args[2], 'options': options}
        if args[0] == 'add':
            phrase['nick'] = nick
            try:
                re.compile(phrase['regex'])
            except:
                return 'Invalid regex: %s' % phrase['regex']
            db.helga_quip.entries.insert(phrase)
        elif args[0] == 'remove':
            db.helga_quip.entries.remove(phrase)
    return random_ack()
Beispiel #3
0
def _quip_manage(client, channel, nick, message, args):
    """ Add/remove quip/phrase to stash """
    if args[0] == 'drop':
        db.helga_quip.entries.drop()
    elif args[0] == 'dump':
        quips = [p['regex'] + ' | ' + p['kind'] + ' | ' + pretty_map(p['options']) for p in db.helga_quip.entries.find()]
        if not quips:
            return "Quip database empty"
        payload = {'title':'helga-quip dump', 'content': '\n'.join(quips)}
        r = requests.post("http://dpaste.com/api/v2/", payload)
        return r.headers['location']
    else:
        valid_options=['-q']

        # Filter out any option that is invalid and turn them into a
        # dict
        options = dict(map(lambda o: (o[:2], o[2:]), filter(lambda o: o[:2] in valid_options, args[3:])))
        
        phrase = {'kind':args[1], 'regex':args[2], 'options':options}
        if args[0] == 'add':
            phrase['nick'] = nick
            try:
                re.compile(phrase['regex'])
            except:
                return 'Invalid regex: %s' % phrase['regex']
            db.helga_quip.entries.insert(phrase)
        elif args[0] == 'remove':
            db.helga_quip.entries.remove(phrase)
    return random_ack()
Beispiel #4
0
def add(syllables, input, author=None):
    logger.info('Adding {0} syllable line: {1}'.format(syllables, input))
    db.haiku.insert({
        'syllables': syllables,
        'message': input,
        'author': author,
    })
    return random_ack()
Beispiel #5
0
def delete_reminder(channel, hash):
    rec = db.reminders.find_one({'hash': hash})

    if rec is not None:
        db.reminders.remove(rec['_id'])
        return random_ack()
    else:
        return u"No reminder found with hash '{0}'".format(hash)
Beispiel #6
0
def add(syllables, input, author=None):
    logger.info('Adding %s syllable line: %s', syllables, input)
    db.haiku.insert({
        'syllables': syllables,
        'message': input,
        'author': author,
    })
    return random_ack()
Beispiel #7
0
def delete_reminder(channel, hash):
    rec = db.reminders.find_one({'hash': hash})

    if rec is not None:
        db.reminders.remove(rec['_id'])
        return random_ack()
    else:
        return "No reminder found with hash '{0}'".format(hash)
Beispiel #8
0
def add(syllables, input, author=None):
    logger.info('Adding %s syllable line: %s', syllables, input)
    db.haiku.insert({
        'syllables': syllables,
        'message': input,
        'author': author,
    })
    return random_ack()
Beispiel #9
0
def add_autojoin(channel):
    logger.info('Adding autojoin channel %s', channel)
    db_opts = {'channel': channel}

    if db.autojoin.find(db_opts).count() == 0:
        db.autojoin.insert(db_opts)
        return random_ack()
    else:
        return "I'm already doing that"
Beispiel #10
0
def add_autojoin(channel):
    logger.info('Adding autojoin channel {0}'.format(channel))
    db_opts = {'channel': channel}

    if db.autojoin.find(db_opts).count() == 0:
        db.autojoin.insert(db_opts)
        return random_ack()
    else:
        return "I'm already doing that"
Beispiel #11
0
def handle_add(client, channel, parser, nick):
    set_args = parser_to_dict(parser)
    # generate some defaults
    if 'id' not in set_args:
        set_args['id'] = generate_id()
    if 'status' not in set_args:
        set_args['status'] = 'pending'
    set_args['last_update'] = str(datetime.now())
    db.team.candidates.insert(set_args)
    return random_ack()
Beispiel #12
0
def handle_update(client, channel, parser, nick):
    get_args = {}
    # if id is passed, use id. name is a fallback.
    if parser.id:
        get_args['id'] = parser.id
    elif parser.name:
        get_args['name'] = parser.name
    set_args = parser_to_dict(parser)
    set_args['last_update'] = str(datetime.now())
    db.team.candidates.find_one_and_update(get_args, {'$set': set_args})
    return random_ack()
Beispiel #13
0
def delete_reminder(channel, id):
    try:
        id = objectid.ObjectId(id)
    except objectid.InvalidId:
        return u"Invalid ID format '{0}'".format(id)

    rec = db.reminders.find_one({'_id': id})

    if rec is not None:
        db.reminders.remove(rec['_id'])
        return random_ack()
    else:
        return u"No reminder found with id '{0}'".format(id)
def logic(args, nick):
    if args[0] == 'point':
        if not '--skip-remove' in args:
            db.helga_poker.entries.remove({'item': args[1], 'nick': nick})
        db.helga_poker.entries.insert({
            'item': args[1],
            'value': args[2],
            'nick': nick
        })
    elif args[0] == 'show':
        if len(args) < 2:
            return 'Sorry, you need to designate an item to show status on!'
        queryset = db.helga_poker.entries.find({'item': args[1]})
        if queryset.count() == 0:
            return 'Sorry, there are no votes for ' + args[1]
        query_nicks = ', '.join(
            [query['nick'] + ':' + query['value'] for query in queryset])
        queryset.rewind()
        values = [int(query['value']) for query in queryset]
        mean = float(sum(values)) / max(len(values), 1)
        median = sorted(values)[int(len(values) / 2)]
        return 'Median:{} avg:{} votes:{}'.format(median, mean, query_nicks)
    elif args[0] == 'status':
        if len(args) < 2:
            return 'Sorry, you need to designate an item to show status on!'
        queryset = db.helga_poker.entries.find({'item': args[1]})
        if queryset.count() == 0:
            return 'No votes for ' + args[1] + ':('
        query_nicks = ', '.join(query['nick'] for query in queryset)
        return '{} votes from: {} for {}'.format(queryset.count(), query_nicks,
                                                 args[1])
    elif args[0] == 'dump':
        results = [
            p['item'] + ' ' + p['value'] + ' ' + p['nick']
            for p in db.helga_poker.entries.find()
        ]
        if not results:
            return "Poker database empty"
        payload = {
            'title': 'helga-pointing-poker dump',
            'content': '\n'.join(results)
        }
        r = requests.post("http://dpaste.com/api/v2/", payload)
        return r.headers['location']
    elif args[0] == 'clear':
        db.helga_poker.entries.drop()
    else:
        return 'Unrecognized poker command: ' + str(args)
    return random_ack()
Beispiel #15
0
def commit_changes(v1, *args):
    """Respect the READONLY setting, return ack, or no perms message
       args is a list of 3-tuples, that will be passed to setattr iff we will write to V1

       Because of the V1Meta caching, strange things happen if you make changes and don't actually commit
       So only make the changes when commiting.
    """
    if getattr(settings, 'VERSIONONE_READONLY', True):
        return 'I would, but I\'m not allowed to write :('

    for call in args:
        setattr(*call)

    v1.commit()
    return random_ack()
Beispiel #16
0
def commit_changes(v1, *args):
    """Respect the READONLY setting, return ack, or no perms message
       args is a list of 3-tuples, that will be passed to setattr iff we will write to V1

       Because of the V1Meta caching, strange things happen if you make changes and don't actually commit
       So only make the changes when commiting.
    """
    if getattr(settings, 'VERSIONONE_READONLY', True):
        return 'I would, but I\'m not allowed to write :('

    for call in args:
        setattr(*call)

    v1.commit()
    return random_ack()
Beispiel #17
0
def elo(client, channel, nick, message, cmg, args):
    if args[0] == 'add' and len(args) == 4:
        game_name = args[1]
        winner_name = args[2]
        loser_name = args[3]
        winner_elo, loser_elo = add_result(game_name, winner_name, loser_name)
        return '{} now has {} elo, {} now has {} elo'.format(winner_name, winner_elo, loser_name, loser_elo)
    elif args[0] == 'list' and len(args) == 3:
        game_name = args[1]
        player_name = args[2]
        player_elo = get_player_elo(game_name, player_name)
        return '{} has {} elo for {}'.format(player_name, player_elo, game_name)
    elif args[0] == 'drop' and len(args) == 2:
        drop_game(args[1])
        return random_ack()
    return "I don't understand args %s" % str(args)
Beispiel #18
0
def alias_command(v1, client, channel, nick, *args):
    # Populate subcmd, and target to continue
    target = nick
    try:
        subcmd = args[0]
        args = args[1:]
    except IndexError:
        # 0 args lookup nick
        subcmd = 'lookup'
    else:
        # At least 1 arg
        if args:
            target = ' '.join(args)
        else:
            # Exactly 1 arg - look it up if not a command
            if subcmd not in ['lookup', 'remove']:
                target = subcmd
                subcmd = 'lookup'

    if subcmd == 'lookup':
        lookup = {'irc_nick': target}
        try:
            v1_nick = db.v1_user_map.find_one(lookup)['v1_nick']
        except (TypeError, KeyError):
            v1_nick = target
        return '{0} is known as {1} in V1'.format(target, v1_nick)

    elif subcmd == 'set':
        lookup = {'irc_nick': nick}
        alias = db.v1_user_map.find_one(lookup) or lookup
        alias['v1_nick'] = target
        db.v1_user_map.save(alias)

    elif subcmd == 'remove':
        if target != nick:
            return 'That\'s not nice {0}. You can\'t remove {1}'.format(
                nick, target)
        lookup = {'irc_nick': nick}
        db.v1_user_map.find_and_modify(lookup, remove=True)
    else:
        return 'No {0}, you can\'t {1}!'.format(nick, subcmd)

    return random_ack()
Beispiel #19
0
def alias_command(v1, client, channel, nick, *args):
    # Populate subcmd, and target to continue
    target = nick
    try:
        subcmd = args[0]
        args = args[1:]
    except IndexError:
        # 0 args lookup nick
        subcmd = 'lookup'
    else:
        # At least 1 arg
        if args:
            target = ' '.join(args)
        else:
            # Exactly 1 arg - look it up if not a command
            if subcmd not in ['lookup', 'remove']:
                target = subcmd
                subcmd = 'lookup'

    if subcmd == 'lookup':
        lookup = {'irc_nick': target}
        try:
            v1_nick = db.v1_user_map.find_one(lookup)['v1_nick']
        except (TypeError, KeyError):
            v1_nick = target
        return '{0} is known as {1} in V1'.format(target, v1_nick)

    elif subcmd == 'set':
        lookup = {'irc_nick': nick}
        alias = db.v1_user_map.find_one(lookup) or lookup
        alias['v1_nick'] = target
        db.v1_user_map.save(alias)

    elif subcmd == 'remove':
        if target != nick:
            return 'That\'s not nice {0}. You can\'t remove {1}'.format(nick, target)
        lookup = {'irc_nick': nick}
        db.v1_user_map.find_and_modify(lookup, remove=True)
    else:
        return 'No {0}, you can\'t {1}!'.format(nick, subcmd)

    return random_ack()
Beispiel #20
0
def oauth_command(v1, client, channel, nick, reply_code=None):
    if not USE_OAUTH:
        return 'Oauth is not enabled'

    client = OAuth2WebServerFlow(
        settings.VERSIONONE_OAUTH_CLIENT_ID,
        settings.VERSIONONE_OAUTH_CLIENT_SECRET,
        'apiv1',  # Scope for XML api
        redirect_uri='urn:ietf:wg:oauth:2.0:oob',
        auth_uri=settings.VERSIONONE_URL + '/oauth.v1/auth',
        token_uri=settings.VERSIONONE_URL + '/oauth.v1/token',
    )

    if reply_code:
        q = {'irc_nick': nick}
        auth_info = db.v1_oauth.find_one(q) or q

        if reply_code == 'forget':
            for key in ['access_token', 'refresh_token', 'token_expiry']:
                try:
                    del (auth_info[key])
                except KeyError:
                    pass
        else:
            try:
                creds = client.step2_exchange(reply_code)
            except FlowExchangeError as e:
                return 'Sorry {0} "{1}" happened. Try "!v1 oauth" again from the start'.format(
                    nick, e)

            # Creds Ok, save the info
            auth_info['access_token'] = creds.access_token
            auth_info['refresh_token'] = creds.refresh_token
            auth_info['token_expiry'] = creds.token_expiry

        db.v1_oauth.save(auth_info)
        return random_ack()

    # No reply_code - show step1 link
    return 'Visit {0} then do "!v1 oauth <code>" with the generated code'.format(
        client.step1_get_authorize_url())
Beispiel #21
0
def team_command(v1, client, channel, nick, *args):
    try:
        subcmd = args[0]
        args = args[1:]
    except IndexError:
        subcmd = 'list'

    # Find the named channel or create new
    q = {'name': channel}
    channel_settings = db.v1_channel_settings.find_one(q) or q
    teams = channel_settings.get('teams', {})
    # NB: White space is lost in command parsing, hope for the best
    name = ' '.join(args)

    if subcmd == 'list':
        return '\n'.join([
            '{0} {1}'.format(t, u) for t, u in teams.iteritems()
        ]) if teams else 'No teams found for {0}'.format(channel)
    elif subcmd == 'add':
        try:
            team = v1.Team.where(Name=name).first()
        except IndexError:
            return 'I\'m sorry {0}, team name "{1}" not found'.format(
                nick, name)
        # Manually building a url is lame, but the url property on TeamRooms doesn't work
        teams[name] = ', '.join([
            '{0}/TeamRoom.mvc/Show/{1}'.format(settings.VERSIONONE_URL,
                                               r.intid) for r in team.Rooms
        ]) or team.url
    elif subcmd == 'remove':
        try:
            del teams[name]
        except KeyError:
            return 'I\'m sorry {0}, team name "{1}" not found for {2}'.format(
                nick, name, channel)
    else:
        return 'No {0}, you can\'t {1}!'.format(nick, subcmd)
    # If we didn't return by now, save teams back to DB, and ack the user
    channel_settings['teams'] = teams
    db.v1_channel_settings.save(channel_settings)
    return random_ack()
Beispiel #22
0
def token_command(v1, client, channel, nick, reply_code=None):
    if reply_code:
        q = {'irc_nick': nick}
        auth_info = db.v1_oauth.find_one(q) or q

        if reply_code == 'forget':
            # remove the token
            try:
                del (auth_info['api_token'])
            except KeyError:
                return 'Token was already gone'
        else:
            # update the token
            auth_info['api_token'] = reply_code
        db.v1_oauth.save(auth_info)
        return random_ack()

    # No reply_code - show step1 instructions
    return (
        'In V1 go to your Applications and generate a Personal Access Token'
        'then do "!v1 token <code>" with the generated code')
Beispiel #23
0
def oauth_command(v1, client, channel, nick, reply_code=None):
    if not USE_OAUTH:
        return 'Oauth is not enabled'

    client = OAuth2WebServerFlow(
        settings.VERSIONONE_OAUTH_CLIENT_ID,
        settings.VERSIONONE_OAUTH_CLIENT_SECRET,
        'apiv1',  # Scope for XML api
        redirect_uri='urn:ietf:wg:oauth:2.0:oob',
        auth_uri=settings.VERSIONONE_URL + '/oauth.v1/auth',
        token_uri=settings.VERSIONONE_URL + '/oauth.v1/token',
    )

    if reply_code:
        q = {'irc_nick': nick}
        auth_info = db.v1_oauth.find_one(q) or q

        if reply_code == 'forget':
            for key in ['access_token', 'refresh_token', 'token_expiry']:
                try:
                    del(auth_info[key])
                except KeyError:
                    pass
        else:
            try:
                creds = client.step2_exchange(reply_code)
            except FlowExchangeError as e:
                return 'Sorry {0} "{1}" happened. Try "!v1 oauth" again from the start'.format(nick, e)

            # Creds Ok, save the info
            auth_info['access_token'] = creds.access_token
            auth_info['refresh_token'] = creds.refresh_token
            auth_info['token_expiry'] = creds.token_expiry

        db.v1_oauth.save(auth_info)
        return random_ack()

    # No reply_code - show step1 link
    return 'Visit {0} then do "!v1 oauth <code>" with the generated code'.format(
        client.step1_get_authorize_url())
Beispiel #24
0
def token_command(v1, client, channel, nick, reply_code=None):
    if reply_code:
        q = {'irc_nick': nick}
        auth_info = db.v1_oauth.find_one(q) or q

        if reply_code == 'forget':
            # remove the token
            try:
                del(auth_info['api_token'])
            except KeyError:
                return 'Token was already gone'
        else:
            # update the token
            auth_info['api_token'] = reply_code
        db.v1_oauth.save(auth_info)
        return random_ack()

    # No reply_code - show step1 instructions
    return (
        'In V1 go to your Applications and generate a Personal Access Token'
        'then do "!v1 token <code>" with the generated code'
    )
Beispiel #25
0
def team_command(v1, client, channel, nick, *args):
    try:
        subcmd = args[0]
        args = args[1:]
    except IndexError:
        subcmd = 'list'

    # Find the named channel or create new
    q = {'name': channel}
    channel_settings = db.v1_channel_settings.find_one(q) or q
    teams = channel_settings.get('teams', {})
    # NB: White space is lost in command parsing, hope for the best
    name = ' '.join(args)

    if subcmd == 'list':
        return '\n'.join([
            '{0} {1}'.format(t, u) for t, u in teams.iteritems()
        ]) if teams else 'No teams found for {0}'.format(channel)
    elif subcmd == 'add':
        try:
            team = v1.Team.where(Name=name).first()
        except IndexError:
            return 'I\'m sorry {0}, team name "{1}" not found'.format(nick, name)
        # Manually building a url is lame, but the url property on TeamRooms doesn't work
        teams[name] = ', '.join([
            '{0}/TeamRoom.mvc/Show/{1}'.format(settings.VERSIONONE_URL, r.intid) for r in team.Rooms
        ]) or team.url
    elif subcmd == 'remove':
        try:
            del teams[name]
        except KeyError:
            return 'I\'m sorry {0}, team name "{1}" not found for {2}'.format(nick, name, channel)
    else:
        return 'No {0}, you can\'t {1}!'.format(nick, subcmd)
    # If we didn't return by now, save teams back to DB, and ack the user
    channel_settings['teams'] = teams
    db.v1_channel_settings.save(channel_settings)
    return random_ack()
Beispiel #26
0
def meet(client, channel, nick, message, cmd, args):
    global _client
    _client = client
    if args[0] == 'status':
        status(args[1], nick, args[2:])
        return nick + ": " + random_ack()
    elif args[0] == 'schedule':
        name = args[1]
        s = args_dict(args[4])  # schedule arguments, e.g. "days 1"
        schedule(name, args[2], args[3], s)
        add_meeting_scheduler(name)
        return random_ack()
    elif args[0] == 'digest':
        name = args[1]
        start_date = args_dict(args[2], True)
        start_date = datetime(**start_date)
        end_date = args_dict(args[3], True)
        end_date = datetime(**end_date)
        # add more filters to find, like nick?
        cursor = db.meet.entries.find({
            'name': name,
            'time': {
                '$gte': start_date,
                '$lt': end_date
            },
        })
        statuses = []
        for p in cursor:
            statuses.append({
                'nick': p['nick'],
                'status': ' '.join(p['status']),
                'time': p['time'].strftime("%Y-%m-%d %H:%M:%S"),
            })
        statuses = [json.dumps(s, sort_keys=True, indent=2) for s in statuses]
        if not statuses:
            return nick + ": query empty"
        payload = {
            'title': 'helga-meet digest',
            'content': '\n'.join(statuses)
        }
        r = requests.post("http://dpaste.com/api/v2/", payload)
        return r.headers['location']
    elif args[0] == 'dump':
        # dump meetup db
        meetups = []
        for meetup in db.meet.meetup.find():
            meetups.append({
                'name': meetup['name'],
                'channel': meetup['channel'],
                'participants': meetup['participants'],
                'cron_interval': meetup['cron_interval'],
            })
        meetups = [json.dumps(s, sort_keys=True, indent=2) for s in meetups]
        if not meetups:
            return nick + ": query empty"
        payload = {
            'title': 'helga-meet meetup dump',
            'content': '\n'.join(meetups)
        }
        r = requests.post("http://dpaste.com/api/v2/", payload)
        return r.headers['location']
    elif args[0] == 'remove':
        if nick in settings.OPERATORS:
            name = args[1]
            entries = len(args) > 2 and args[2] == 'entries'
            remove(name, entries)
            scheduler.remove_job('meeting_monitor_' + name)
            return random_ack()
        return "Sorry " + nick + ", you don't have permission to do that"
    return "I don't understand this meet request"
Beispiel #27
0
def remove_autojoin(channel):
    logger.info('Removing Autojoin {0}'.format(channel))
    db.autojoin.remove({'channel': channel})
    return random_ack()
Beispiel #28
0
def remove(syllables, input):
    logger.info('Removing %s syllable line: %s', syllables, input)
    db.haiku.remove({'syllables': syllables, 'message': input})
    return random_ack()
Beispiel #29
0
def remove(syllables, input):
    logger.info('Removing %s syllable line: %s', syllables, input)
    db.haiku.remove({'syllables': syllables, 'message': input})
    return random_ack()
Beispiel #30
0
def remove_autojoin(channel):
    logger.info('Removing autojoin %s', channel)
    db.autojoin.remove({'channel': channel})
    return random_ack()
Beispiel #31
0
def remove_autojoin(channel):
    logger.info('Removing autojoin %s', channel)
    db.autojoin.remove({'channel': channel})
    return random_ack()
Beispiel #32
0
def remove(syllables, input):
    logger.info('Removing {0} syllable line: {1}'.format(syllables, input))
    db.haiku.remove({'syllables': syllables, 'message': input})
    return random_ack()
Beispiel #33
0
def handle_remove(client, channel, parser, nick):
    get_args = parser_to_dict(parser)
    db.team.candidates.remove(get_args)
    return random_ack()