""" The help command. The basic idea is that help texts for commands are best written by those that write the commands - the admins. So command-help is all auto-loaded and searched from the current command set. The normal, database-tied help system is used for collaborative creation of other help topics such as RP help or game-world aides. """ from collections import defaultdict from src.utils.utils import fill, dedent from src.commands.command import Command from src.help.models import HelpEntry from src.utils import create from src.commands.default.muxcommand import MuxCommand # limit symbol import for API __all__ = ("CmdHelp", "CmdSetHelp") LIST_ARGS = ("list", "all") SEP = "{C" + "-"*78 + "{n" def format_help_entry(title, help_text, aliases=None, suggested=None): """ This visually formats the help entry. """ string = SEP + "\n" if title: string += "{CHelp topic for {w%s{n" % (title.capitalize()) if aliases: string += " {C(aliases: {w%s{n{C){n" % (", ".join(aliases)) if help_text: string += "\n%s" % dedent(help_text.rstrip()) if suggested: string += "\n\n{CSuggested:{n " string += "{w%s{n" % fill(", ".join(suggested)) string.strip() string += "\n" + SEP return string def format_help_list(hdict_cmds, hdict_db): """ Output a category-ordered list. The input are the pre-loaded help files for commands and database-helpfiles resectively. """ string = "" if hdict_cmds and hdict_cmds.values(): string += "\n" + SEP + "\n {CCommand help entries{n\n" + SEP for category in sorted(hdict_cmds.keys()): string += "\n {w%s{n:\n" % (str(category).capitalize()) string += "{G" + fill(", ".join(sorted(hdict_cmds[category]))) + "{n" if hdict_db and hdict_db.values(): string += "\n\n" + SEP + "\n\r {COther help entries{n\n" + SEP for category in sorted(hdict_db.keys()): string += "\n\r {w%s{n:\n" % (str(category).capitalize()) string += "{G" + fill(", ".join(sorted([str(topic) for topic in hdict_db[category]]))) + "{n" return string class CmdHelp(Command): """ The main help command Usage: help help list help all This will search for help on commands and other topics related to the game. """ key = "help" locks = "cmd:all()" # this is a special cmdhandler flag that makes the cmdhandler also pack # the current cmdset with the call to self.func(). return_cmdset = True def parse(self): """ input is a string containing the command or topic to match. """ self.original_args = self.args.strip() self.args = self.args.strip().lower() def func(self): """ Run the dynamic help entry creator. """ query, cmdset = self.args, self.cmdset caller = self.caller if not query: query = "all" # removing doublets in cmdset, caused by cmdhandler # having to allow doublet commands to manage exits etc. cmdset.make_unique(caller) # Listing all help entries if query in LIST_ARGS: # we want to list all available help entries, grouped by category. hdict_cmd = defaultdict(list) for cmd in (cmd for cmd in cmdset if cmd.auto_help and not cmd.is_exit and not cmd.key.startswith('__') and cmd.access(caller)): hdict_cmd[cmd.help_category].append(cmd.key) hdict_db = defaultdict(list) for topic in (topic for topic in HelpEntry.objects.get_all_topics() if topic.access(caller, 'view', default=True)): hdict_db[topic.help_category].append(topic.key) help_entry = format_help_list(hdict_cmd, hdict_db) caller.msg(help_entry) return # Look for a particular help entry # Cmd auto-help dynamic entries cmdmatches = [cmd for cmd in cmdset if query in cmd and cmd.auto_help and cmd.access(caller)] if len(cmdmatches) > 1: # multiple matches. Try to limit it down to exact match cmdmatches = [cmd for cmd in cmdmatches if cmd == query] or cmdmatches # Help-database static entries dbmatches = [topic for topic in HelpEntry.objects.find_topicmatch(query, exact=False) if topic.access(caller, 'view', default=True)] if len(dbmatches) > 1: # try to get unique match dbmatches = [topic for topic in HelpEntry.objects.find_topicmatch(query, exact=True) if topic.access(caller, 'view', default=True)] or dbmatches # Handle result if (not cmdmatches) and (not dbmatches): # no normal match. Check if this is a category match instead categ_cmdmatches = [cmd.key for cmd in cmdset if query == cmd.help_category and cmd.access(caller)] categ_dbmatches = [topic.key for topic in HelpEntry.objects.find_topics_with_category(query) if topic.access(caller, 'view', default=True)] cmddict = None dbdict = None if categ_cmdmatches: cmddict = {query:categ_cmdmatches} if categ_dbmatches: dbdict = {query:categ_dbmatches} if cmddict or dbdict: help_entry = format_help_list(cmddict, dbdict) else: help_entry = "No help entry found for '%s'" % self.original_args elif len(cmdmatches) == 1: # we matched against a unique command name or alias. Show its help entry. suggested = [] if dbmatches: suggested = [entry.key for entry in dbmatches] cmd = cmdmatches[0] help_entry = format_help_entry(cmd.key, cmd.__doc__, aliases=cmd.aliases, suggested=suggested) elif len(dbmatches) == 1: # matched against a database entry entry = dbmatches[0] help_entry = format_help_entry(entry.key, entry.entrytext) else: # multiple matches of either type cmdalts = [cmd.key for cmd in cmdmatches] dbalts = [entry.key for entry in dbmatches] helptext = "Multiple help entries match your search ..." help_entry = format_help_entry("", helptext, None, cmdalts + dbalts) # send result to user caller.msg(help_entry) class CmdSetHelp(MuxCommand): """ @help - edit the help database Usage: @help[/switches] [,category[,locks]] = Switches: add - add or replace a new topic with text. append - add text to the end of topic with a newline between. merge - As append, but don't add a newline between the old text and the appended text. delete - remove help topic. force - (used with add) create help topic also if the topic already exists. Examples: @sethelp/add throw = This throws something at ... @sethelp/append pickpocketing,Thievery,is_thief, is_staff) = This steals ... @sethelp/append pickpocketing, ,is_thief, is_staff) = This steals ... """ key = "@help" aliases = "@sethelp" locks = "cmd:perm(PlayerHelpers)" help_category = "Building" def func(self): "Implement the function" caller = self.caller switches = self.switches lhslist = self.lhslist rhs = self.rhs if not self.args: caller.msg("Usage: @sethelp/[add|del|append|merge] [,category[,locks,..] = ]") return topicstr = "" category = "" lockstring = "" try: topicstr = lhslist[0] category = lhslist[1] lockstring = ",".join(lhslist[2:]) except Exception: pass if not topicstr: caller.msg("You have to define a topic!") return string = "" #print topicstr, category, lockstring if switches and switches[0] in ('append', 'app','merge'): # add text to the end of a help topic # find the topic to append to old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr) if not old_entry: string = "Could not find topic '%s'. You must give an exact name." % topicstr else: old_entry = old_entry[0] entrytext = old_entry.entrytext if switches[0] == 'merge': old_entry.entrytext = "%s %s" % (entrytext, self.rhs) string = "Added the new text right after the old one (merge)." else: old_entry.entrytext = "%s\n\n%s" % (entrytext, self.rhs) string = "Added the new text as a new paragraph after the old one (append)" old_entry.save() elif switches and switches[0] in ('delete','del'): #delete a help entry old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr) if not old_entry: string = "Could not find topic '%s'." % topicstr else: old_entry[0].delete() string = "Deleted the help entry '%s'." % topicstr else: # add a new help entry. force_create = ('for' in switches) or ('force' in switches) old_entry = None try: old_entry = HelpEntry.objects.get(key=topicstr) except Exception: pass if old_entry: if force_create: old_entry.key = topicstr old_entry.entrytext = self.rhs old_entry.help_category = category old_entry.locks.clear() old_entry.locks.add(lockstring) old_entry.save() string = "Overwrote the old topic '%s' with a new one." % topicstr else: string = "Topic '%s' already exists. Use /force to overwrite it." % topicstr else: # no old entry. Create a new one. new_entry = create.create_help_entry(topicstr, rhs, category, lockstring) if new_entry: string = "Topic '%s' was successfully created." % topicstr else: string = "Error when creating topic '%s'! Maybe it already exists?" % topicstr # give feedback caller.msg(string)