def namegen(text, bot, notice): """[generator|list] - generates some names using the chosen generator, or lists all generators if 'list' is specified :type bot: cloudbot.bot.CloudBot """ # clean up the input inp = text.strip().lower() # get a list of available name generators files = os.listdir(os.path.join(bot.data_dir, "name_files")) all_modules = [ os.path.splitext(i)[0] for i in files if os.path.splitext(i)[1] == ".json" ] all_modules.sort() # command to return a list of all available generators if inp == "list": message = "Available generators: " message += formatting.get_text_list(all_modules, 'and') notice(message) return if inp: selected_module = inp.split()[0] else: # make some generic fantasy names selected_module = "fantasy" # check if the selected module is valid if selected_module not in all_modules: return "{} is not a valid name generator.".format(inp) # load the name generator path = os.path.join(bot.data_dir, "name_files", "{}.json".format(selected_module)) with codecs.open(path, encoding="utf-8") as f: try: generator = get_generator(f.read()) except ValueError as error: return "Unable to read name file: {}".format(error) # time to generate some names name_list = generator.generate_strings(10) # and finally return the final message :D return "Some names to ponder: {}.".format( formatting.get_text_list(name_list, 'and'))
def cmdinfo(text, bot, reply): """<command> - Gets various information about a command""" cmd = text.split()[0].lower().strip() if cmd in bot.plugin_manager.commands: cmd_hook = bot.plugin_manager.commands[cmd] else: potentials = [] for potential_match, plugin in bot.plugin_manager.commands.items(): if potential_match.startswith(cmd): potentials.append((potential_match, plugin)) if potentials: if len(potentials) == 1: cmd_hook = potentials[0][1] else: reply("Possible matches: {}".format( formatting.get_text_list( sorted([command for command, plugin in potentials])))) return else: cmd_hook = None if cmd_hook is None: reply("Unknown command: '{}'".format(cmd)) return hook_name = cmd_hook.plugin.title + "." + cmd_hook.function_name info = "Command: {}, Aliases: [{}], Hook name: {}".format( cmd_hook.name, ', '.join(cmd_hook.aliases), hook_name) if cmd_hook.permissions: info += ", Permissions: [{}]".format(', '.join(cmd_hook.permissions)) reply(info)
def run_extra_prefix(event, bot, conn, chan, content): """ :type event: cloudbot.events.BaseEvent :type bot: cloudbot.bot.CloudBot :type conn: cloudbot.connection.Connection :type chan: str :type content: str """ key = (conn.name, chan) if key in chan_re: match = chan_re[key].match(content) if match: command = match.group(2).lower() if command in bot.plugin_manager.commands: command_hook = bot.plugin_manager.commands[command] event = CommandEvent(triggered_command=command, hook=command_hook, text=match.group(3).strip(), base_event=event) yield from bot.plugin_manager.launch(command_hook, event) else: potential_matches = [] for potential_match, plugin in bot.plugin_manager.commands.items(): if potential_match.startswith(command): potential_matches.append((potential_match, plugin)) if potential_matches: if len(potential_matches) == 1: command_hook = potential_matches[0][1] event = CommandEvent(triggered_command=command, hook=command_hook, text=match.group(3).strip(), base_event=event) yield from bot.plugin_manager.launch(command_hook, event) else: event.notice("Possible matches: {}".format( formatting.get_text_list([command for command, plugin in potential_matches])))
def bancount(text, bot, loop): """<user> - gets a count of <user>'s minecraft bans from fishbans""" user = text.strip() headers = {'User-Agent': bot.user_agent} try: _func = functools.partial(requests.get, api_url.format(quote_plus(user)), headers=headers) request = yield from loop.run_in_executor(None, _func) request.raise_for_status() except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: return "Could not fetch ban data from the Fishbans API: {}".format(e) try: json = request.json() except ValueError: return "Could not fetch ban data from the Fishbans API: Invalid Response" user_url = "http://fishbans.com/u/{}/".format(user) services = json["stats"]["service"] out = [] for service, ban_count in list(services.items()): if ban_count != 0: out.append("{}: \x02{}\x02".format(service, ban_count)) else: pass if not out: return "The user \x02{}\x02 has no bans - {}".format(user, user_url) else: return "Bans for \x02{}\x02: {} - {}".format(user, formatting.get_text_list(out, "and"), user_url)
def bancount(text, bot, loop): """<user> - gets a count of <user>'s minecraft bans from fishbans""" user = text.strip() headers = {'User-Agent': bot.user_agent} try: _func = functools.partial(requests.get, api_url.format(quote_plus(user)), headers=headers) request = yield from loop.run_in_executor(None, _func) request.raise_for_status() except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: return "Could not fetch ban data from the Fishbans API: {}".format(e) try: json = request.json() except ValueError: return "Could not fetch ban data from the Fishbans API: Invalid Response" user_url = "http://fishbans.com/u/{}/".format(user) services = json["stats"]["service"] out = [] for service, ban_count in list(services.items()): if ban_count != 0: out.append("{}: \x02{}\x02".format(service, ban_count)) else: pass if not out: return "The user \x02{}\x02 has no bans - {}".format(user, user_url) else: return "Bans for \x02{}\x02: {} - {}".format( user, formatting.get_text_list(out, "and"), user_url)
def format_time(seconds, count=3, accuracy=6, simple=False): """ Takes a length of time in seconds and returns a string describing that length of time. This function has a number of optional arguments that can be combined: SIMPLE: displays the time in a simple format >> format_time(SECONDS) 1 hour, 2 minutes and 34 seconds >> format_time(SECONDS, simple=True) 1h 2m 34s COUNT: how many periods should be shown (default 3) >> format_time(SECONDS) 147 years, 9 months and 8 weeks >> format_time(SECONDS, count=6) 147 years, 9 months, 7 weeks, 18 hours, 12 minutes and 34 seconds """ if simple: periods = [('c', 60 * 60 * 24 * 365 * 100), ('de', 60 * 60 * 24 * 365 * 10), ('y', 60 * 60 * 24 * 365), ('m', 60 * 60 * 24 * 30), ('d', 60 * 60 * 24), ('h', 60 * 60), ('m', 60), ('s', 1)] else: periods = [(('century', 'centuries'), 60 * 60 * 24 * 365 * 100), (('decade', 'decades'), 60 * 60 * 24 * 365 * 10), (('year', 'years'), 60 * 60 * 24 * 365), (('month', 'months'), 60 * 60 * 24 * 30), (('day', 'days'), 60 * 60 * 24), (('hour', 'hours'), 60 * 60), (('minute', 'minutes'), 60), (('second', 'seconds'), 1)] periods = periods[-accuracy:] strings = [] i = 0 for period_name, period_seconds in periods: if i < count: if seconds > period_seconds: period_value, seconds = divmod(seconds, period_seconds) i += 1 if simple: strings.append("{}{}".format(period_value, period_name)) else: if period_value == 1: strings.append("{} {}".format(period_value, period_name[0])) else: strings.append("{} {}".format(period_value, period_name[1])) else: break if simple: return " ".join(strings) else: return formatting.get_text_list(strings, "and")
def namegen(text, bot, notice): """[generator|list] - generates some names using the chosen generator, or lists all generators if 'list' is specified :type bot: cloudbot.bot.CloudBot """ # clean up the input inp = text.strip().lower() # get a list of available name generators files = os.listdir(os.path.join(bot.data_dir, "name_files")) all_modules = [os.path.splitext(i)[0] for i in files if os.path.splitext(i)[1] == ".json"] all_modules.sort() # command to return a list of all available generators if inp == "list": message = "Available generators: " message += formatting.get_text_list(all_modules, 'and') notice(message) return if inp: selected_module = inp.split()[0] else: # make some generic fantasy names selected_module = "fantasy" # check if the selected module is valid if selected_module not in all_modules: return "{} is not a valid name generator.".format(inp) # load the name generator path = os.path.join(bot.data_dir, "name_files", "{}.json".format(selected_module)) with codecs.open(path, encoding="utf-8") as f: try: generator = get_generator(f.read()) except ValueError as error: return "Unable to read name file: {}".format(error) # time to generate some names name_list = generator.generate_strings(10) # and finally return the final message :D return "Some names to ponder: {}.".format(formatting.get_text_list(name_list, 'and'))
def remove_user_from_group(user, group, event): permission_manager = event.conn.permissions changed_masks = permission_manager.remove_group_user( group.lower(), user.lower()) mask_list = formatting.get_text_list(changed_masks, 'and') event.reply("Removed {} from {}".format(mask_list, group)) event.admin_log("{} used deluser remove {} from {}.".format( event.nick, mask_list, group)) return bool(changed_masks)
def format(self, simple=True, skip_empty=True, count=3): i = 0 out = [] for num, unit in self.parts: if i >= count: break if num <= 0 and skip_empty: continue i += 1 out.append(unit.format(num, simple=simple)) if simple: return " ".join(out) return formatting.get_text_list(out, "and")
def format_count(nicks, masks, hosts, addresses, is_admin, duration): counts = [ (len(nicks), 'nick'), (len(masks), 'mask'), ] if is_admin: counts.extend([ (len(hosts), 'host'), (len(addresses), 'address'), ]) if all(count == 0 for count, thing in counts): return "None." else: return "Done. Found {} in {:.3f} seconds".format( get_text_list( [pluralize_auto(count, thing) for count, thing in counts], 'and'), duration)
def poll(text, conn, nick, chan, message, reply, triggered_prefix): """{<question>[: <option1>, <option2>[, <option3>]...|close} - Creates a poll for [question] with the provided options (default: Yes, No), or closes the poll if the argument is 'close'""" global polls # get poll ID uid = ":".join([conn.name, chan, nick]).lower() if text.lower() == "close": if uid not in polls.keys(): return "You have no active poll to close." p = polls.get(uid) reply("Poll closed. Final results for \x02'{}'\x02:".format( p.question, p.creator)) message(p.format_results()) del polls[uid] return if uid in polls.keys(): return "You already have an active poll in this channel, you must close it before you can create a new one." if ':' in text: question, options = text.strip().split(':') c = findall(r'([^,]+)', options) if len(c) == 1: c = findall(r'(\S+)', options) options = list(set(x.strip() for x in c)) _poll = Poll(question, nick, options) else: question = text.strip() _poll = Poll(question, nick) # store poll in list polls[uid] = _poll option_str = get_text_list( [option.title for option in _poll.options.values()], "and") message( 'Created poll \x02\"{}\"\x02 with the following options: {}'.format( _poll.question, option_str)) message("Use {prefix}vote {nick} <option> to vote on this poll!".format( prefix=triggered_prefix, nick=nick.lower()))
def format_results_or_paste(terms, duration, nicks, masks, hosts, addresses, is_admin, paste=None): if isinstance(terms, str): terms = [terms] terms_list = get_text_list(["'{}'".format(term) for term in terms], 'and') yield "Results for {}:".format(terms_list) lines = list(format_results(nicks, masks, hosts, addresses, is_admin)) if (len(lines) > 5 and paste is not False) or paste is True: yield do_paste(paste_results(nicks, masks, hosts, addresses, is_admin)) else: yield from lines yield format_count(nicks, masks, hosts, addresses, is_admin, duration)
def poll(text, conn, nick, chan, message, reply): global polls # get poll ID uid = ":".join([conn.name, chan, nick]).lower() if text.lower() == "close": if uid not in polls.keys(): return "You have no active poll to close." p = polls.get(uid) reply("Your poll has been closed. Final results for \x02\"{}\"\x02:". format(p.question, p.creator)) message(p.format_results()) del polls[uid] return if uid in polls.keys(): return "You already have an active poll in this channel, you must close it before you can create a new one." if ':' in text: question, options = text.strip().split(':') c = findall(r'([^,]+)', options) if len(c) == 1: c = findall(r'(\S+)', options) options = list(set(x.strip() for x in c)) _poll = Poll(question, nick, options) else: question = text.strip() _poll = Poll(question, nick) # store poll in list polls[uid] = _poll option_str = get_text_list( [option.title for option in _poll.options.values()], "and") message( 'Created poll \x02\"{}\"\x02 with the following options: {}'.format( _poll.question, option_str)) message("Use .vote {} <option> to vote on this poll!".format(nick.lower()))
def remove_fact(chan, names, db, notice): found = {} missing = [] for name in names: data = factoid_cache[chan].get(name.lower()) if data: found[name] = data else: missing.append(name) if missing: notice("Unknown factoids: {}".format( get_text_list([repr(s) for s in missing], 'and') )) if found: try: notice("Removed Data: {}".format(paste_facts(found, True))) except NoPasteException: notice("Unable to paste removed data, not removing facts") return del_factoid(db, chan, list(found.keys()))
def poll(text, conn, nick, chan, message, reply): global polls # get poll ID uid = ":".join([conn.name, chan, nick]).lower() if text.lower() == "close": if uid not in polls.keys(): return "You have no active poll to close." p = polls.get(uid) reply("Your poll has been closed. Final results for \x02\"{}\"\x02:".format(p.question, p.creator)) message(p.format_results()) del polls[uid] return if uid in polls.keys(): return "You already have an active poll in this channel, you must close it before you can create a new one." if ':' in text: question, options = text.strip().split(':') c = findall(r'([^,]+)', options) if len(c) == 1: c = findall(r'(\S+)', options) options = list(set(x.strip() for x in c)) _poll = Poll(question, nick, options) else: question = text.strip() _poll = Poll(question, nick) # store poll in list polls[uid] = _poll option_str = get_text_list([option.title for option in _poll.options.values()], "and") message('Created poll \x02\"{}\"\x02 with the following options: {}'.format(_poll.question, option_str)) message("Use @vote {} <option> to vote on this poll!".format(nick.lower()))
async def cmdinfo(text, bot, notice): """<command> - Gets various information about a command""" name = text.split()[0] cmds = list(get_potential_commands(bot, name)) if not cmds: notice("Unknown command: '{}'".format(name)) return if len(cmds) > 1: notice("Possible matches: {}".format( formatting.get_text_list( sorted([command for command, plugin in cmds])))) return cmd_hook = cmds[0][1] hook_name = cmd_hook.plugin.title + "." + cmd_hook.function_name info = "Command: {}, Aliases: [{}], Hook name: {}".format( cmd_hook.name, ', '.join(cmd_hook.aliases), hook_name) if cmd_hook.permissions: info += ", Permissions: [{}]".format(', '.join(cmd_hook.permissions)) notice(info)
def process(self, event): """ :type event: Event """ run_before_tasks = [] tasks = [] command_prefix = event.conn.config.get('command_prefix', '.') # Raw IRC hook for raw_hook in self.plugin_manager.catch_all_triggers: # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument if not raw_hook.threaded: run_before_tasks.append( self.plugin_manager.launch( raw_hook, Event(hook=raw_hook, base_event=event))) else: tasks.append( self.plugin_manager.launch( raw_hook, Event(hook=raw_hook, base_event=event))) if event.irc_command in self.plugin_manager.raw_triggers: for raw_hook in self.plugin_manager.raw_triggers[ event.irc_command]: tasks.append( self.plugin_manager.launch( raw_hook, Event(hook=raw_hook, base_event=event))) # Event hooks if event.type in self.plugin_manager.event_type_hooks: for event_hook in self.plugin_manager.event_type_hooks[event.type]: tasks.append( self.plugin_manager.launch( event_hook, Event(hook=event_hook, base_event=event))) if event.type is EventType.message: # Commands if event.chan.lower() == event.nick.lower( ): # private message, no command prefix command_re = r'(?i)^(?:[{}]?|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format( command_prefix, event.conn.nick) else: command_re = r'(?i)^(?:[{}]|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format( command_prefix, event.conn.nick) cmd_match = re.match(command_re, event.content) if cmd_match: command = cmd_match.group(1).lower() if command in self.plugin_manager.commands: command_hook = self.plugin_manager.commands[command] command_event = CommandEvent( hook=command_hook, text=cmd_match.group(2).strip(), triggered_command=command, base_event=event) tasks.append( self.plugin_manager.launch(command_hook, command_event)) else: potential_matches = [] for potential_match, plugin in self.plugin_manager.commands.items( ): if potential_match.startswith(command): potential_matches.append((potential_match, plugin)) if potential_matches: if len(potential_matches) == 1: command_hook = potential_matches[0][1] command_event = CommandEvent( hook=command_hook, text=cmd_match.group(2).strip(), triggered_command=command, base_event=event) tasks.append( self.plugin_manager.launch( command_hook, command_event)) else: event.notice("Possible matches: {}".format( formatting.get_text_list([ command for command, plugin in potential_matches ]))) # Regex hooks for regex, regex_hook in self.plugin_manager.regex_hooks: if not regex_hook.run_on_cmd and cmd_match: pass else: regex_match = regex.search(event.content) if regex_match: regex_event = RegexEvent(hook=regex_hook, match=regex_match, base_event=event) tasks.append( self.plugin_manager.launch(regex_hook, regex_event)) # Run the tasks yield from asyncio.gather(*run_before_tasks, loop=self.loop) yield from asyncio.gather(*tasks, loop=self.loop)
def process(self, event): """ :type event: Event """ run_before_tasks = [] tasks = [] command_prefix = event.conn.config.get('command_prefix', '.') # Raw IRC hook for raw_hook in self.plugin_manager.catch_all_triggers: # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument if not raw_hook.threaded: run_before_tasks.append( self.plugin_manager.launch(raw_hook, Event(hook=raw_hook, base_event=event))) else: tasks.append(self.plugin_manager.launch(raw_hook, Event(hook=raw_hook, base_event=event))) if event.irc_command in self.plugin_manager.raw_triggers: for raw_hook in self.plugin_manager.raw_triggers[event.irc_command]: tasks.append(self.plugin_manager.launch(raw_hook, Event(hook=raw_hook, base_event=event))) # Event hooks if event.type in self.plugin_manager.event_type_hooks: for event_hook in self.plugin_manager.event_type_hooks[event.type]: tasks.append(self.plugin_manager.launch(event_hook, Event(hook=event_hook, base_event=event))) if event.type is EventType.message: # Commands if event.chan.lower() == event.nick.lower(): # private message, no command prefix command_re = r'(?i)^(?:[{}]?|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format(command_prefix, event.conn.nick) else: command_re = r'(?i)^(?:[{}]|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format(command_prefix, event.conn.nick) cmd_match = re.match(command_re, event.content) if cmd_match: command = cmd_match.group(1).lower() if command in self.plugin_manager.commands: command_hook = self.plugin_manager.commands[command] command_event = CommandEvent(hook=command_hook, text=cmd_match.group(2).strip(), triggered_command=command, base_event=event) tasks.append(self.plugin_manager.launch(command_hook, command_event)) else: potential_matches = [] for potential_match, plugin in self.plugin_manager.commands.items(): if potential_match.startswith(command): potential_matches.append((potential_match, plugin)) if potential_matches: if len(potential_matches) == 1: command_hook = potential_matches[0][1] command_event = CommandEvent(hook=command_hook, text=cmd_match.group(2).strip(), triggered_command=command, base_event=event) tasks.append(self.plugin_manager.launch(command_hook, command_event)) else: event.notice("Possible matches: {}".format( formatting.get_text_list([command for command, plugin in potential_matches]))) # Regex hooks for regex, regex_hook in self.plugin_manager.regex_hooks: if not regex_hook.run_on_cmd and cmd_match: pass else: regex_match = regex.search(event.content) if regex_match: regex_event = RegexEvent(hook=regex_hook, match=regex_match, base_event=event) tasks.append(self.plugin_manager.launch(regex_hook, regex_event)) # Run the tasks yield from asyncio.gather(*run_before_tasks, loop=self.loop) yield from asyncio.gather(*tasks, loop=self.loop)
def format_time(seconds, count=3, accuracy=6, simple=False): """ Takes a length of time in seconds and returns a string describing that length of time. This function has a number of optional arguments that can be combined: SIMPLE: displays the time in a simple format >>> format_time(SECONDS) 1 hour, 2 minutes and 34 seconds >>> format_time(SECONDS, simple=True) 1h 2m 34s COUNT: how many periods should be shown (default 3) >>> format_time(SECONDS) 147 years, 9 months and 8 weeks >>> format_time(SECONDS, count=6) 147 years, 9 months, 7 weeks, 18 hours, 12 minutes and 34 seconds """ if simple: periods = [ ('c', 60 * 60 * 24 * 365 * 100), ('de', 60 * 60 * 24 * 365 * 10), ('y', 60 * 60 * 24 * 365), ('m', 60 * 60 * 24 * 30), ('d', 60 * 60 * 24), ('h', 60 * 60), ('m', 60), ('s', 1) ] else: periods = [ (('century', 'centuries'), 60 * 60 * 24 * 365 * 100), (('decade', 'decades'), 60 * 60 * 24 * 365 * 10), (('year', 'years'), 60 * 60 * 24 * 365), (('month', 'months'), 60 * 60 * 24 * 30), (('day', 'days'), 60 * 60 * 24), (('hour', 'hours'), 60 * 60), (('minute', 'minutes'), 60), (('second', 'seconds'), 1) ] periods = periods[-accuracy:] strings = [] i = 0 for period_name, period_seconds in periods: if i < count: if seconds > period_seconds: period_value, seconds = divmod(seconds, period_seconds) i += 1 if simple: strings.append("{}{}".format(period_value, period_name)) else: if period_value == 1: strings.append("{} {}".format(period_value, period_name[0])) else: strings.append("{} {}".format(period_value, period_name[1])) else: break if simple: return " ".join(strings) else: return formatting.get_text_list(strings, "and")
def test_get_text_list(): assert get_text_list(['a', 'b', 'c', 'd']) == 'a, b, c or d' assert get_text_list(['a', 'b', 'c'], 'and') == 'a, b and c' assert get_text_list(['a', 'b'], 'and') == 'a and b' assert get_text_list(['a']) == 'a' assert get_text_list([]) == ''
def test_get_text_list(): assert get_text_list(["a", "b", "c", "d"]) == "a, b, c or d" assert get_text_list(["a", "b", "c"], "and") == "a, b and c" assert get_text_list(["a", "b"], "and") == "a and b" assert get_text_list(["a"]) == "a" assert get_text_list([]) == ""
def process(self, event): """ :type event: Event """ run_before_tasks = [] tasks = [] command_prefix = event.conn.config.get('command_prefix', '.') halted = False def add_hook(hook, _event, _run_before=False): nonlocal halted if halted: return False coro = self.plugin_manager.launch(hook, _event) if _run_before: run_before_tasks.append(coro) else: tasks.append(coro) if hook.action is Action.HALTALL: halted = True return False elif hook.action is Action.HALTTYPE: return False return True # Raw IRC hook for raw_hook in self.plugin_manager.catch_all_triggers: # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument run_before = not raw_hook.threaded if not add_hook(raw_hook, Event(hook=raw_hook, base_event=event), _run_before=run_before): # The hook has an action of Action.HALT* so stop adding new tasks break if event.irc_command in self.plugin_manager.raw_triggers: for raw_hook in self.plugin_manager.raw_triggers[ event.irc_command]: if not add_hook(raw_hook, Event(hook=raw_hook, base_event=event)): # The hook has an action of Action.HALT* so stop adding new tasks break # Event hooks if event.type in self.plugin_manager.event_type_hooks: for event_hook in self.plugin_manager.event_type_hooks[event.type]: if not add_hook(event_hook, Event(hook=event_hook, base_event=event)): # The hook has an action of Action.HALT* so stop adding new tasks break if event.type is EventType.message: # Commands if event.chan.lower() == event.nick.lower( ): # private message, no command prefix command_re = r'(?i)^(?:[{}]?|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)' else: command_re = r'(?i)^(?:[{}]|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)' cmd_match = re.match( command_re.format(command_prefix, event.conn.nick), event.content_raw) if cmd_match: command = cmd_match.group(1).lower() text = irc_clean(cmd_match.group(2).strip()) if command in self.plugin_manager.commands: command_hook = self.plugin_manager.commands[command] command_event = CommandEvent(hook=command_hook, text=text, triggered_command=command, base_event=event) add_hook(command_hook, command_event) else: potential_matches = [] for potential_match, plugin in self.plugin_manager.commands.items( ): if potential_match.startswith(command): potential_matches.append((potential_match, plugin)) if potential_matches: if len(potential_matches) == 1: command_hook = potential_matches[0][1] command_event = CommandEvent( hook=command_hook, text=text, triggered_command=command, base_event=event) add_hook(command_hook, command_event) else: event.notice("Possible matches: {}".format( formatting.get_text_list([ command for command, plugin in potential_matches ]))) # Regex hooks regex_matched = False for regex, regex_hook in self.plugin_manager.regex_hooks: if not regex_hook.run_on_cmd and cmd_match: continue if regex_hook.only_no_match and regex_matched: continue regex_match = regex.search(event.content) if regex_match: regex_matched = True regex_event = RegexEvent(hook=regex_hook, match=regex_match, base_event=event) if not add_hook(regex_hook, regex_event): # The hook has an action of Action.HALT* so stop adding new tasks break # Run the tasks yield from asyncio.gather(*run_before_tasks, loop=self.loop) yield from asyncio.gather(*tasks, loop=self.loop)
async def help_command(text, chan, bot, notice, message, has_permission, triggered_prefix): """[command] - gives help for [command], or lists all available commands if no command is specified :type chan: str :type text: str :type bot: cloudbot.bot.CloudBot """ if text: searching_for = text.lower().strip() else: searching_for = None if text: cmds = list(get_potential_commands(bot, text)) if not cmds: notice("Unknown command '{}'".format(text)) return if len(cmds) > 1: notice("Possible matches: {}".format( formatting.get_text_list( sorted([command for command, _ in cmds])))) return doc = cmds[0][1].doc if doc: notice("{}{} {}".format(triggered_prefix, searching_for, doc)) else: notice("Command {} has no additional documentation.".format( searching_for)) else: commands = [] for plugin in sorted(set(bot.plugin_manager.commands.values()), key=attrgetter("name")): # use set to remove duplicate commands (from multiple aliases), and sorted to sort by name if plugin.permissions: # check permissions allowed = False for perm in plugin.permissions: if has_permission(perm, notice=False): allowed = True break if not allowed: # skip adding this command continue # add the command to lines sent command = plugin.name commands.append(command) # list of lines to send to the user lines = formatting.chunk_str( "Here's a list of commands you can use: " + ", ".join(commands)) for line in lines: if chan[:1] == "#": notice(line) else: # This is an user in this case. message(line) notice( "For detailed help, use {}help <command>, without the brackets.". format(triggered_prefix))
async def process(self, event): """ :type event: Event """ run_before_tasks = [] tasks = [] halted = False def add_hook(hook, _event, _run_before=False): nonlocal halted if halted: return False if hook.clients and _event.conn.type not in hook.clients: return True coro = self.plugin_manager.launch(hook, _event) if _run_before: run_before_tasks.append(coro) else: tasks.append(coro) if hook.action is Action.HALTALL: halted = True return False if hook.action is Action.HALTTYPE: return False return True # Raw IRC hook for raw_hook in self.plugin_manager.catch_all_triggers: # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument run_before = not raw_hook.threaded if not add_hook(raw_hook, Event(hook=raw_hook, base_event=event), _run_before=run_before): # The hook has an action of Action.HALT* so stop adding new tasks break if event.irc_command in self.plugin_manager.raw_triggers: for raw_hook in self.plugin_manager.raw_triggers[ event.irc_command]: if not add_hook(raw_hook, Event(hook=raw_hook, base_event=event)): # The hook has an action of Action.HALT* so stop adding new tasks break # Event hooks if event.type in self.plugin_manager.event_type_hooks: for event_hook in self.plugin_manager.event_type_hooks[event.type]: if not add_hook(event_hook, Event(hook=event_hook, base_event=event)): # The hook has an action of Action.HALT* so stop adding new tasks break matched_command = False if event.type is EventType.message: # Commands cmd_match = get_cmd_regex(event).match(event.content) if cmd_match: command_prefix = event.conn.config.get('command_prefix', '.') prefix = cmd_match.group('prefix') or command_prefix[0] command = cmd_match.group('command').lower() text = cmd_match.group('text').strip() cmd_event = partial(CommandEvent, text=text, triggered_command=command, base_event=event, cmd_prefix=prefix) if command in self.plugin_manager.commands: command_hook = self.plugin_manager.commands[command] command_event = cmd_event(hook=command_hook) add_hook(command_hook, command_event) matched_command = True else: potential_matches = [] for potential_match, plugin in self.plugin_manager.commands.items( ): if potential_match.startswith(command): potential_matches.append((potential_match, plugin)) if potential_matches: matched_command = True if len(potential_matches) == 1: command_hook = potential_matches[0][1] command_event = cmd_event(hook=command_hook) add_hook(command_hook, command_event) else: commands = sorted( command for command, plugin in potential_matches) txt_list = formatting.get_text_list(commands) event.notice( "Possible matches: {}".format(txt_list)) if event.type in (EventType.message, EventType.action): # Regex hooks regex_matched = False for regex, regex_hook in self.plugin_manager.regex_hooks: if not regex_hook.run_on_cmd and matched_command: continue if regex_hook.only_no_match and regex_matched: continue regex_match = regex.search(event.content) if regex_match: regex_matched = True regex_event = RegexEvent(hook=regex_hook, match=regex_match, base_event=event) if not add_hook(regex_hook, regex_event): # The hook has an action of Action.HALT* so stop adding new tasks break # Run the tasks await asyncio.gather(*run_before_tasks, loop=self.loop) await asyncio.gather(*tasks, loop=self.loop)