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)
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()
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()
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()
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)
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()
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)
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"
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"
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()
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()
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()
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()
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)
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()
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()
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())
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()
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')
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())
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' )
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()
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"
def remove_autojoin(channel): logger.info('Removing Autojoin {0}'.format(channel)) db.autojoin.remove({'channel': channel}) return random_ack()
def remove(syllables, input): logger.info('Removing %s syllable line: %s', syllables, input) db.haiku.remove({'syllables': syllables, 'message': input}) return random_ack()
def remove_autojoin(channel): logger.info('Removing autojoin %s', channel) db.autojoin.remove({'channel': channel}) return random_ack()
def remove(syllables, input): logger.info('Removing {0} syllable line: {1}'.format(syllables, input)) db.haiku.remove({'syllables': syllables, 'message': input}) return random_ack()
def handle_remove(client, channel, parser, nick): get_args = parser_to_dict(parser) db.team.candidates.remove(get_args) return random_ack()