def log(self, irc_c, msg): chname = "private" if msg.raw_channel is None else msg.raw_channel if msg.kind == 'PRIVMSG': print("[{}] {} <{}> {}".format( time.strftime("%H:%M:%S"), parse.nickColor(chname), parse.nickColor(msg.nick), msg.message, )) elif msg.kind == 'NICK': print("[{}] {} changed their name to {}".format( time.strftime("%H:%M:%S"), parse.nickColor(msg.nick), parse.nickColor(msg.args), )) else: print("[{}] {} {} {}".format( time.strftime("%H:%M:%S"), parse.nickColor(msg.nick), "joined" if msg.kind == 'JOIN' else "left", parse.nickColor(chname), )) try: if not gimmick(msg.message): DB.log_message(msg) except: irc_c.RAW("PRIVMSG #tars A logging error has occurred.") raise
def debug(self, irc_c, msg): msg = parse.output(msg) if not msg: return print("[{}] --> {}: {}".format( time.strftime('%H:%M:%S'), parse.nickColor(msg['channel']), msg['message'], )) if "IDENTIFY" in msg['message']: return msg = { 'channel': msg['channel'] if msg['channel'].startswith('#') else None, 'sender': CONFIG.nick, 'kind': "PRIVMSG", 'message': msg['message'], 'nick': CONFIG.nick, 'timestamp': int(time.time()), } try: DB.log_message(msg) except: irc_c.RAW("PRIVMSG #tars A logging error has occurred.") raise
def record_names(self, irc_c, msg): # msg.args is a string # "TARS = #channel :name1 name2 name3 name4" nicks = re.split(r"\s:?", msg.args.strip()) nicks = nicks[2:] channel = nicks.pop(0) names = [{'nick': name} for name in nicks] # chatstaff names start with a punctuation for key, name in enumerate(names): if name['nick'][0] in '+%@&~': names[key] = { 'nick': name['nick'][1:], 'mode': name['nick'][0], } else: names[key] = {'nick': name['nick'], 'mode': None} # just need to log these names to the db now nameprint("Updating NAMES for {}: {}".format( nickColor(channel), ", ".join(nickColor(nick) for nick in sorted(nicks)), )) try: DB.sort_names(channel, names) except Exception as e: irc_c.RAW("PRIVMSG #tars NAMES error: " + str(e)) raise # broadcast this info to whatever needs it emit_signal(irc_c, 'NAMES_RESPONSE', data=channel)
def execute(self, irc_c, msg, cmd): if 'title' not in self and 'url' not in self: raise CommandError( "At least one of --title or --url must be given.") if 'title' in self: title = self['title'] elif 'url' in self: try: title = DB.get_article_info( DB.get_articles([{ 'type': 'url', # Handle case where the whole URL was entered 'term': self['url'].split("/")[-1], }])[0])['title'] except IndexError as error: raise MyFaultError("I don't see any page at '{}'.".format( self['url'])) from error pages = [ DB.get_article_info(p_id)['title'] for p_id in DB.get_articles([]) ] single_string = Shortest.get_substring(title, pages) helen_style = Shortest.get_multi_substring(title, pages) if single_string is None and helen_style is None: raise MyFaultError( "There's no unique search for \"{}\".".format(title)) msg.reply("Shortest search for \"\x1d{}\x0F\" · {}".format( title, Shortest.pick_answer(single_string, helen_style), ))
def execute(self, irc_c, msg, cmd): # Note that the INVITE event is in plugins/parsemessages.py irc_c.JOIN(self['channel']) msg.reply("Joining {}".format(self['channel'])) irc_c.PRIVMSG(self['channel'], "Joining by request of {}".format(msg.nick)) DB.join_channel(self['channel'])
def change_name(self, irc_c, msg): assert msg.kind == 'NICK' nameprint("{} changed their name to {}".format(msg.nick, msg.args)) try: DB.rename_user(msg.nick, msg.args) except Exception as e: irc_c.RAW("PRIVMSG #tars NAMES error: " + str(e)) raise
def get_wiki_data_for_pages(slugs, **kwargs): """Gets wiki data for the pages indicated by the list of slugs.""" reply = kwargs.pop('reply', lambda _: None) reply(f"Gettings wiki data for {len(slugs)} specific pages") for slug in slugs: page = SCPWiki.get_one_page_meta(slug) DB.add_article(page, commit=False) DB.commit()
def execute(self, irc_c, msg, cmd): if 'message' in self: leavemsg = self['message'] else: leavemsg = None if self['channel'] is not None: channel = self['channel'] msg.reply("Leaving {}".format(self['channel'])) else: channel = msg.raw_channel irc_c.PART(channel, message=leavemsg) DB.leave_channel(channel)
def execute(self, irc_c, msg, cmd): if Refactor.has_refactored and not self['force']: raise CommandError("Already refactored once this reload.") try: if 'sql' in self: DB.issue(self['sql'], callback=msg.reply) else: Refactor.refactor_database() Refactor.has_refactored = True except: msg.reply("Refactoring failed.") raise msg.reply("Refactoring succeeded.")
def get_gib_sentence(self, attempts=0, limit=7500): print("Getting a gib sentence") # messages = [] # for channel in cls.channels: # print("Iterating channels") # for user in cls.users: # print("Iterating users") # messages.extend(DB.get_messages(channel, user)) messages = DB.get_messages( self['channel'], minlength=40, limit=limit, senders=None if self['user'] == [] else self['user'], patterns=[r.pattern for r in self['regex']], ) print("messages found: {}".format(len(messages))) if len(messages) == 0: raise AttributeError # Automatically decrement the state size if the higher state size fails for decr in range(0, self['size']): print("Making model from messages, size {}".format(self['size'] - decr)) # The model cache depends on the state size, so if there is a state # size decrement, discard it anyway if decr != 0: Gib.cache['model'] = None model = (Gib.cache['model'] if Gib.cache['model'] is not None else Gib.make_model(messages, self['size'], decrement=decr)) print("Making sentence") sentence = model.make_short_sentence(400, self['minlength'], tries=200, force_result=False) if sentence is not None: break print("Sentence is None") if not self['no_cache'] and sentence in DB.get_gibs(): print("Sentence already sent, {} attempts remaining".format( CONFIG['gib']['attempt_limit'] - attempts)) try: if attempts < CONFIG['gib']['attempt_limit']: sentence = self.get_gib_sentence(attempts + 1, limit) else: raise RecursionError except RecursionError as error: raise MyFaultError( "I didn't find any gibs for that selection " "that haven't already been said.") from error if sentence is not None: DB.add_gib(sentence) return sentence
def execute(self, irc_c, msg, cmd): if 'channel' in self: channel = self['channel'] else: channel = msg.raw_channel # Issue a fresh NAMES request and await the response get_users(irc_c, channel) try: response = await_signal(irc_c, 'NAMES_RESPONSE', timeout=5.0) # returned data is the channel name assert response == channel except (TimeoutError, AssertionError): # response to success/failure is the same, so doesn't matter pass finally: members = DB.get_occupants(channel, True, levels=True) modes = "+%@&~" members = [ nick for nick, mode in members if mode is not None and modes.find(mode) >= modes.find(self['target']) # Unspecified target is "" so this is genius honestly ] if 'message' in self: for member in members: irc_c.PRIVMSG( member, "{1} in {2} says: {0}".format(self['message'], msg.sender, msg.raw_channel), ) msg.reply("Message sent to selected users.") else: msg.reply("{}: ping!".format(", ".join(members)))
def should_defer(cmd): """Evaluates whether or not a given command should execute. Returns True if the command should defer. Returns False if the command should not defer, i.e. it is okay to execute. The command does not execute if the prefix and alias are shared by another bot, unless the bot was pinged. """ # If there is no command string, there's no need to defer if cmd.command is None: return False # If the bot was pinged, forget everything else - this command is happening if cmd.ping: return False # Filter the deferral configs by the bots that are currently present members = DB.get_channel_members(cmd.channel) defer_configs = [ config for config in CONFIG['deferral'] if config['name'] in members and config['name'] != CONFIG['nick'] and config['prefix'] == cmd.prefix ] # Check if this command has the same name as any matching commands if any(cmd.command.lower() in [command.lower() for command in config['commands']] for config in defer_configs): return True return False
def media_roulette(self): """Get a random image or video link.""" # take all the messages in the channel, filtered for links messages = DB.get_messages(self['channel'], senders=self['user'], patterns=[_URL_PATT]) if len(messages) == 0: raise MyFaultError( "I didn't find any URLs in the selection criteria.") # then reduce strings containing urls to urls urls = [ re.search(_URL_PATT, message, re.IGNORECASE).group(0) for message in messages ] # make urls unique urls = list(set(urls)) # then filter by either images or videos if self['media'] == 'image': urls = [url for url in urls if _IMG_PATT.search(url)] if len(urls) == 0: raise MyFaultError("I didn't find any images.") if self['media'] == 'youtube': urls = [url for url in urls if _YT_PATT.search(url)] if len(urls) == 0: raise MyFaultError("I didn't find any video links.") return urls
def is_controller(cmd): """Checks if the sender of the given command is a bot controller. Avoid using this if possible. Prefer defining permission levels on commands and arguments. Only use this if finer control is needed (e.g. if the permission level depends on the value of an argument). """ return cmd.sender in DB.get_controllers()
def get_wiki_data(**kwargs): """Gets wiki data for all pages.""" reply = kwargs.pop('reply', lambda _: None) if 'seconds' in kwargs: reply(f"Getting wiki data for last {kwargs['seconds']} seconds") else: reply("Getting wiki data for all pages") pages_generator = SCPWiki.get_all_pages(**kwargs) for pages in pages_generator: for page in pages: if page['fullname'].startswith("fragment:"): # Don't want to track fragments continue DB.add_article(page, commit=False) # Give the API a moment to rest gevent.sleep(5) DB.commit()
def execute(self, irc_c, msg, cmd): page_ids = DB.get_showmore_list(msg.raw_channel) if len(page_ids) == 0: raise MyFaultError("I have nothing to show more of.") if self['index'] > len(page_ids): raise MyFaultError( "I only have {} results for the last search.".format( len(page_ids))) pages = [DB.get_article_info(page_id) for page_id in page_ids] if self['index'] == 0: msg.reply("{} saved results (use ..sm to choose): {}".format( len(pages), Showmore.parse_multiple_titles(pages))) else: msg.reply("{}/{} · {}".format( self['index'], len(page_ids), Showmore.parse_title(pages[self['index'] - 1]), ))
def execute(self, irc_c, msg, cmd): message = " ".join(self['message']) if message.startswith("/"): if msg.raw_channel != self['recipient']: raise CommandError( "When executing a command with ..say, the recipient must " "be the current channel ({}), as a safety check.".format( msg.raw_channel)) # This is an IRC command msg.reply("Issuing that...") # Send the message without the leading slash irc_c.RAW(message[1:]) else: if self['obfuscate'] and msg.raw_channel is not None: message = Gib.obfuscate(message, DB.get_aliases(None) + ["ops"]) irc_c.PRIVMSG(self['recipient'], message) if self['recipient'] != msg.raw_channel: msg.reply("Saying that to {}".format(self['recipient']))
def autojoin(self, irc_c, msg): if ("Password accepted" in msg.message or "You are now identified" in msg.message) and CONFIG.channels.db: for channel in DB.get_autojoins(): irc_c.JOIN(channel) nsprint("Joining " + str(channel))
def execute(self, irc_c, msg, cmd): self['channel'] = self['channel'] if len(self['channel']) == 0: if msg.raw_channel is None: # Happens when gibbing from PMs raise CommandError( "Specify a channel to gib from with --channel/-c.") # Default channel is the current one self['channel'] = [msg.raw_channel] limit = self['limit'] or CONFIG['gib']['message_limit'] or 5000 if 'media' in self: limit = -1 if self['me']: self['regex'].append(re.compile(r"^\u0001ACTION ")) # can only gib a channel both the user and the bot are in for channel in self['channel']: if channel == msg.raw_channel: continue if (msg.raw_channel is not None and self['channel'][0] != 'all' and not all(nick in DB.get_channel_members(channel) for nick in [msg.sender, CONFIG.nick])): raise CommandError( "We both must be in a channel in order to gib it.") if (msg.raw_channel is not None and channel != msg.raw_channel and not is_controller(cmd)): raise CommandError( "You can only gib the current channel (or " "any channel from if you do it in PMs with me).") # Does the model need to be regenerated? if not self['no_cache'] and all([ Gib.cache['model'] is not None, Gib.cache['channels'] == self['channel'], Gib.cache['users'] == self['user'], Gib.cache['size'] == self['size'], Gib.cache['limit'] == limit, ]): print("Reusing Markov model") else: Gib.cache['model'] = None Gib.cache['channels'] = self['channel'] Gib.cache['users'] = self['user'] Gib.cache['size'] = self['size'] Gib.cache['limit'] = limit # are we gibbing or rouletting? if 'media' in self: urls = self.media_roulette() msg.reply("{} {} · ({} link{} found)".format( emojize(":game_die:"), random.choice(urls), len(urls), "s" if len(urls) > 1 else "", )) return # gibbing: try: sentence = self.get_gib_sentence(limit=limit) if sentence is None: raise AttributeError except (RuntimeError, AttributeError) as error: raise MyFaultError( "Looks like {} spoken enough in {} just yet.{}".format( ("you haven't" if msg.sender in self['user'] and len(self['user']) == 1 else "nobody has" if len(self['user']) == 0 else "{} hasn't".format(self['user'][0]) if len(self['user']) == 1 else "they haven't"), (self['channel'][0] if (len(self['channel']) == 1 and self['channel'][0] == msg.raw_channel) else "that channel" if len(self['channel']) == 1 else "those channels"), " ({} messages)".format( len(Gib.cache['model'].to_dict()['parsed_sentences'] ) if Gib.cache['model'] is not None else 0), )) from error # first: remove a ping at the beginning of the sentence pattern = r"^(\S+[:,]\s+)(.*)$" match = re.match(pattern, sentence) if match: sentence = match.group(2).strip() # second: modify any words that match the names of channel members sentence = Gib.obfuscate(sentence, DB.get_channel_members(msg.raw_channel)) # match any unmatched pairs sentence = Gib.bracketify(sentence, (r"\"\b", "\""), (r"\b[.!?]*\"", "\"")) sentence = Gib.bracketify(sentence, (r"`\b", "`"), (r"\b[.!?]*`", "`")) sentence = Gib.bracketify(sentence, (r"\(", "("), (r"\)", ")")) sentence = Gib.bracketify(sentence, (r"\[", "["), (r"\}", "]")) sentence = Gib.bracketify(sentence, (r"\{", "{"), (r"\}", "}")) sentence = Gib.bracketify(sentence, ("“", "“"), (r"”", "”")) sentence = Gib.bracketify(sentence, ("‘", "‘"), (r"’", "’")) sentence = Gib.bracketify(sentence, ("«", "«"), (r"»", "»")) cmd.command = cmd.command.lower() if "oo" in cmd.command: sentence = re.sub(r"[aeiou]", "oob", sentence) elif "o" in cmd.command: sentence = re.sub(r"[aeiou]", "ob", sentence) if cmd.command.startswith("b") and cmd.command.endswith("g"): sentence = sentence.upper() msg.reply(sentence)
def part_names(self, irc_c, msg): # make sure the names are always up to date for channel in DB.get_all_channels(): get_users(irc_c, channel)
def execute(self, irc_c, msg, cmd): if self['nick'] == "": self['nick'] = msg.sender # 1. Check if this is the right nick # get the user ID user_id = DB.get_user_id(self['nick']) if user_id is None: msg.reply("I don't know anyone called '{}'.".format(self['nick'])) return # 2. Add new aliases if len(self['add']) > 0: if self['nick'].lower() != msg.sender.lower( ) and not is_controller(cmd): raise CommandError("You can't add an alias for someone else.") for alias in self['add']: if DB.add_alias(user_id, alias, 1): msg.reply("{} already has the alias {}!".format( self['nick'], alias)) msg.reply("Added aliases to {}: {}".format( self['nick'], ", ".join(self['alias']))) irc_c.PRIVMSG( CONFIG['channels']['home'], "{} added alias {}".format(self['nick'], self['alias']), ) if len(self['remove']) > 0: if self['nick'].lower() != msg.sender.lower( ) and not is_controller(cmd): raise CommandError( "You can't remove an alias from someone else.") for alias in self['remove']: if not DB.remove_alias(user_id, alias, 1): msg.reply("{} didn't have the alias {}!".format( self['nick'], alias)) msg.reply("Removed aliases from {}: {}".format( self['nick'], ", ".join(self['remove']))) if 'wikiname' in self: if self['nick'].lower() != msg.sender.lower( ) and not is_controller(cmd): raise CommandError("You can't change someone else's wikiname.") # The wikiname might be taken by another user wikiname_owner = DB.get_wikiname_owner(self['wikiname']) if wikiname_owner is not None and wikiname_owner != user_id: raise MyFaultError( "Another user has claimed that Wikidot username. " "If you think this was in error, contact {} " "immediately.".format(CONFIG['IRC']['owner'])) current_wikiname = DB.get_wikiname(user_id) if current_wikiname == self['wikiname']: raise MyFaultError("{} Wikidot username is already {}.".format( "{}'s".format(self['nick']) if self['nick'].lower() != msg.sender.lower() else "Your", self['wikiname'], )) DB.set_wikiname(user_id, self['wikiname']) msg.reply("Updated {} Wikidot username {}to {}.".format( "{}'s".format(self['nick']) if self['nick'].lower() != msg.sender.lower() else "your", "" if current_wikiname is None else "from {} ".format(current_wikiname), self['wikiname'], )) irc_c.PRIVMSG( CONFIG['channels']['home'], "{} set wikiname {}".format(self['nick'], self['wikiname']), ) if self['list']: # get all aliases associated with the user aliases = DB.get_aliases(user_id) wikiname = DB.get_wikiname(user_id) msg.reply("I've seen {} go by the names: {}. {}".format( self['nick'] if self['nick'] != msg.sender else "you", ", ".join(aliases), "I don't know {} Wikidot username.".format( "their" if self['nick'] != msg.sender else "your", ) if wikiname is None else "{} Wikidot username is {}.".format( "Their" if self['nick'] != msg.sender else "Your", wikiname, ), ))
def execute(self, irc_c, msg, cmd): # Set the return mode of the output selection = { 'ignorepromoted': self['ignorepromoted'], 'order': self['order'], 'limit': self['limit'], 'offset': self['offset'], } if self['random']: selection['order'] = 'random' selection['limit'] = 1 # if self['recommend']: # selection['order'] = 'recommend' # selection['limit'] = 1 if self['newest']: selection['order'] = 'recent' selection['limit'] = 1 # What are we searching for? searches = [] strings = [] if len(self['title']) > 0: strings = self['title'] searches.extend([{'term': s, 'type': None} for s in strings]) # Add any regexes regexes = [] for regex in self['regex']: try: re.compile(regex) except re.error as e: raise CommandError( "'{}' isn't a valid regular expression: {}.".format( regex, e ) ) from e regexes.append(regex) # don't append compiled regex - SQL doesn't like that searches.extend([{'term': r, 'type': 'regex'} for r in regexes]) # Set the tags tags = {'include': [], 'exclude': []} for tag in self['tags']: if tag[0] == "-": tags['exclude'].append(tag[1:]) continue if tag[0] == "+": tags['include'].append(tag[1:]) continue tags['include'].append(tag) searches.append({'term': tags, 'type': 'tags'}) # Set the author authors = {'include': [], 'exclude': []} for author in self['author']: if author[0] == "-": authors['exclude'].append(author[1:]) continue if author[0] == "+": authors['include'].append(author[1:]) continue authors['include'].append(author) searches.append({'term': authors, 'type': 'author'}) # Set the rating # Cases to account for: modifiers, range, combination ratings = MinMax() for rating in self['rating']: if ".." in rating: rating = rating.split("..") if len(rating) > 2: raise CommandError("Too many ratings in range.") try: rating = [int(x) for x in rating] except ValueError as e: raise CommandError( "Ratings in a range must be integers." ) from e try: ratings >= min(rating) ratings <= max(rating) except MinMaxError as e: raise CommandError(str(e).format("rating")) from e elif rating[0] in [">", "<", "="]: pattern = r"^(?P<comp>[<>=]{1,2})(?P<value>[0-9]+)" match = re.search(pattern, rating) if match: try: rating = int(match.group('value')) except ValueError as e: raise CommandError("Invalid rating comparison.") from e comp = match.group('comp') try: if comp == ">=": ratings >= rating elif comp == "<=": ratings <= rating elif comp == "<": ratings < rating elif comp == ">": ratings > rating elif comp == "=": ratings >= rating ratings <= rating else: raise CommandError("Unknown rating comparison.") except MinMaxError as e: raise CommandError(str(e).format("rating")) from e elif rating[0] in [">", "<", "="]: pattern = r"^(?P<comp>[<>=]{1,2})(?P<value>-?[0-9]+)" match = re.search(pattern, rating) if match: try: rating = int(match.group('value')) except ValueError: raise CommandError("Invalid rating comparison.") comp = match.group('comp') try: if comp == ">=": ratings >= rating elif comp == "<=": ratings <= rating elif comp == "<": ratings < rating elif comp == ">": ratings > rating elif comp == "=": ratings >= rating ratings <= rating else: raise CommandError( "Unknown operator in rating comparison." ) except MinMaxError as e: raise CommandError(str(e).format("rating")) else: raise CommandError("Invalid rating comparison.") else: raise CommandError("Invalid rating comparison.") else: try: rating = int(rating) except ValueError as e: raise CommandError( "Rating must be a range, comparison, or number." ) from e # Assume =, assign both try: ratings >= rating ratings <= rating except MinMaxError as e: raise CommandError(str(e).format("rating")) searches.append({'term': ratings, 'type': 'rating'}) # Set created date # Cases to handle: absolute, relative, range (which can be both) createds = MinMax() created = self['created'] # created is a list of date selectors - ranges, abs and rels # but ALL dates are ranges! created = [DateRange(c) for c in created] # created is now a list of DateRanges with min and max try: for selector in created: if selector.max is not None: createds <= selector.max if selector.min is not None: createds >= selector.min except MinMaxError as e: raise CommandError(str(e).format("date")) searches.append({'term': createds, 'type': 'date'}) # Set category categories = {'include': [], 'exclude': []} for category in self['category']: if category[0] == "-": categories['exclude'].append(category[1:]) continue else: if category[0] == "+": categories['include'].append(category[1:]) continue categories['include'].append(category) continue categories['include'].append(category) searches.append({'term': categories, 'type': 'category'}) # Set parent page parents = self['parent'] if parents is not None: searches.append({'term': parents, 'type': 'parent'}) # FINAL BIT - summarise commands if self['verbose']: verbose = "Searching for articles " if len(strings) > 0: verbose += "containing \"{}\"; ".format("\", \"".join(strings)) if len(regexes) > 0: verbose += "matching the regex /{}/; ".format( "/ & /".join(regexes) ) if parents is not None: verbose += "whose parent page is '{}'; ".format(parents) if len(categories['include']) == 1: verbose += ( "in the category '" + categories['include'][0] + "'; " ) elif len(categories['include']) > 1: verbose += ( "in the categories '" + "', '".join(categories) + "; " ) if len(categories['exclude']) == 1: verbose += ( "not in the category '" + categories['exclude'][0] + "'; " ) elif len(categories['exclude']) > 1: verbose += ( "not in the categories '" + "', '".join(categories) + "; " ) if len(tags['include']) > 0: verbose += ( "with the tags '" + "', '".join(tags['include']) + "'; " ) if len(tags['exclude']) > 0: verbose += ( "without the tags '" + "', '".join(tags['exclude']) + "'; " ) if len(authors['include']) > 0: verbose += "by " + " & ".join(authors['include']) + "; " if len(authors['exclude']) > 0: verbose += "not by " + " or ".join(authors['exclude']) + "; " if ratings['max'] is not None and ratings['min'] is not None: if ratings['max'] == ratings['min']: verbose += "with a rating of " + str(ratings['max']) + "; " else: verbose += ( "with a rating between " + str(ratings['min']) + " and " + str(ratings['max']) + "; " ) elif ratings['max'] is not None: verbose += ( "with a rating less than " + str(ratings['max'] + 1) + "; " ) elif ratings['min'] is not None: verbose += ( "with a rating greater than " + str(ratings['min'] - 1) + "; " ) if createds['min'] is not None and createds['max'] is not None: verbose += ( "created between " + createds['min'].to_datetime_string() + " and " + createds['max'].to_datetime_string() + "; " ) elif createds['max'] is not None: verbose += ( "created before " + createds['max'].to_datetime_string() + "; " ) elif createds['min'] is not None: verbose += ( "created after " + createds['min'].to_datetime_string() + "; " ) if verbose.endswith("; "): verbose = verbose[:-2] msg.reply(verbose) page_ids = DB.get_articles(searches) pages = [DB.get_article_info(p_id) for p_id in page_ids] pages = Search.order(pages, search_term=strings, **selection) if len(pages) >= 50: msg.reply( "{} results found - you're going to have to be more " "specific!".format(len(pages)) ) return if len(pages) > 3: msg.reply( "{} results (use ..sm to choose): {}".format( len(pages), Showmore.parse_multiple_titles(pages) ) ) DB.set_showmore_list(msg.raw_channel, [p['id'] for p in pages]) return if len(pages) == 0: # check if there's no args other than --verbose if len(self['title']) > 0: # google only takes 10 args url = google_search( '"' + '" "'.join(self['title'][:10]) + '"', num=1 )[0] if url is None: msg.reply("No matches found.") return if url['title'].endswith(" - SCP Foundation"): url['title'] = url['title'][:-17] msg.reply( "No matches found. Did you mean \x02{}\x0F? {}".format( url['title'], url['link'] ) ) else: msg.reply("No matches found.") return for page in pages: msg.reply( Gib.obfuscate( Showmore.parse_title(page), DB.get_channel_members(msg.raw_channel), ) )
def execute(self, irc_c, msg, cmd): ##### ping matches ##### if cmd.ping: if any(x in cmd.message.lower() for x in [ "f**k you", "piss off", "f**k off", ]): msg.reply("{}: no u".format(msg.nick)) return ##### ping-optional text matches ##### if cmd.message.startswith("?? "): # CROM compatibility # Override defer by pretending the bot was pinged cmd.ping = True # Manually parse and instantiate a search command return parsemessages.try_command(irc_c, cmd.message[3:], cmd, 'search') if msg.message.lower() == "{}!".format(CONFIG.nick.lower()): msg.reply("{}!".format(msg.nick)) return if strip(msg.message.lower()) in [ strip("{}{}".format(g, CONFIG.nick.lower())) for g in greets ]: if msg.sender == 'XilasCrowe': msg.reply("toast") return msg.reply(greet(msg.nick)) return if (CONFIG.nick == "TARS" and matches_any_of( msg.message, [ "what does tars stand for?", "is tars an acronym?", ], ) and "TARS" in msg.message.upper()): msg.reply(acronym()) return if (CONFIG.nick == "TARS" and matches_any_of(msg.message, [ "is tars a bot?", "tars are you a bot?", ]) and "TARS" in msg.message.upper()): msg.reply("Yep.") return if (CONFIG.nick == "TARS" and matches_any_of(msg.message, [ "is tars a person?", "tars are you a person?", ]) and "TARS" in msg.message.upper()): msg.reply("Nope. I'm a bot.") return if (CONFIG.nick == "TARS" and matches_any_of(msg.message, [ "what is your iq", ]) and "TARS" in msg.message.upper()): msg.reply("big") return if (CONFIG.nick == "TARS" and matches_any_of(msg.message, [ "damn you to hell", ]) and "TARS" in msg.message.upper()): msg.reply("damn me to hell") return ##### regex matches ##### # give url for reddit links match = re.search(r"(?:^|\s)/?r/(\S*)", msg.message, re.IGNORECASE) if match: msg.reply("https://www.reddit.com/r/{}".format(match.group(1))) return # tell me about new acronyms acronyms = find_acronym(msg.message, CONFIG['IRC']['nick']) if acronyms: raw_acronym, bold_acronym = acronyms with open(CONFIG['converse']['acronyms'], 'r') as acro_file: acros = json.load(acro_file) existing_acronyms = [acro['acronym'] for acro in acros] if raw_acronym not in existing_acronyms: msg.reply(bold_acronym) if msg.raw_channel != CONFIG['channels']['home']: cmd.context.RAW("PRIVMSG {} {}".format( CONFIG['channels']['home'], bold_acronym)) with open(CONFIG['converse']['acronyms'], 'w') as acro_file: acros.append({ 'acronym': raw_acronym, 'sender': msg.nick, 'channel': msg.raw_channel, 'date': msg.timestamp, }) json.dump(acros, acro_file, indent=2) return ##### custom matches ##### if (msg.sender == "Jazstar" and "slime" in msg.message and "XilasCrowe" in DB.get_channel_members(msg.raw_channel)): msg.reply("Oi xilas I heard you like slime!") return # after all attempts, must indicate failure if pinged if cmd.ping: return 1