mirror of
https://github.com/evennia/evennia.git
synced 2026-03-23 08:16:30 +01:00
257 lines
9.7 KiB
Python
257 lines
9.7 KiB
Python
"""
|
|
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.utils.utils import string_suggestions
|
|
from src.commands.default.muxcommand import MuxCommand
|
|
|
|
# limit symbol import for API
|
|
__all__ = ("CmdHelp", "CmdSetHelp")
|
|
|
|
|
|
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
|
|
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 any(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).title())
|
|
string += "{G" + fill(", ".join(sorted(hdict_cmds[category]))) + "{n"
|
|
if hdict_db and any(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).title())
|
|
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 <topic or command>
|
|
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
|
|
|
|
suggestion_cutoff = 0.6
|
|
suggestion_maxnum = 5
|
|
|
|
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)
|
|
|
|
# retrieve all available commands and database topics
|
|
all_cmds = [cmd for cmd in cmdset if cmd.auto_help and cmd.access(caller)]
|
|
all_topics = [topic for topic in HelpEntry.objects.all() if topic.access(caller, 'view', default=True)]
|
|
all_categories = list(set([cmd.help_category.lower() for cmd in all_cmds] + [topic.help_category.lower() for topic in all_topics]))
|
|
|
|
if query in ("list", "all"):
|
|
# we want to list all available help entries, grouped by category
|
|
hdict_cmd = defaultdict(list)
|
|
hdict_topic = defaultdict(list)
|
|
# create the dictionaries {category:[topic, topic ...]} required by format_help_list
|
|
[hdict_cmd[cmd.help_category].append(cmd.key) for cmd in all_cmds]
|
|
[hdict_topic[topic.help_category].append(topic.key) for topic in all_topics]
|
|
# report back
|
|
self.msg(format_help_list(hdict_cmd, hdict_topic))
|
|
return
|
|
|
|
# Try to access a particular command
|
|
|
|
# build vocabulary of suggestions and rate them by string similarity.
|
|
vocabulary = [cmd.key for cmd in all_cmds if cmd] + [topic.key for topic in all_topics] + all_categories
|
|
[vocabulary.extend(cmd.aliases) for cmd in all_cmds]
|
|
suggestions = [sugg for sugg in string_suggestions(query, set(vocabulary), cutoff=suggestion_cutoff, maxnum=suggestion_maxnum)
|
|
if sugg != query]
|
|
if not suggestions:
|
|
suggestions = [sugg for sugg in vocabulary if sugg != query and sugg.startswith(query)]
|
|
|
|
# try an exact command auto-help match
|
|
match = [cmd for cmd in all_cmds if cmd == query]
|
|
if len(match) == 1:
|
|
self.msg(format_help_entry(match[0].key, match[0].__doc__, aliases=match[0].aliases, suggested=suggestions))
|
|
return
|
|
|
|
# try an exact database help entry match
|
|
match = list(HelpEntry.objects.find_topicmatch(query, exact=True))
|
|
if len(match) == 1:
|
|
self.msg(format_help_entry(match[0].key, match[0].entrytext, suggested=suggestions))
|
|
return
|
|
|
|
# try to see if a category name was entered
|
|
if query in all_categories:
|
|
self.msg(format_help_list({query:[cmd.key for cmd in all_cmds if cmd.help_category==query]},
|
|
{query:[topic.key for topic in all_topics if topic.help_category==query]}))
|
|
return
|
|
|
|
# no exact matches found. Just give suggestions.
|
|
self.msg(format_help_entry("", "No help entry found for '%s'" % query, None, suggested=suggestions))
|
|
|
|
class CmdSetHelp(MuxCommand):
|
|
"""
|
|
@help - edit the help database
|
|
|
|
Usage:
|
|
@help[/switches] <topic>[,category[,locks]] = <text>
|
|
|
|
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 = This steals ...
|
|
@sethelp/append pickpocketing, ,attr(is_thief) = This steals ...
|
|
|
|
This command manipulates the help database. A help entry can be created,
|
|
appended/merged to and deleted. If you don't assign a category, the "General"
|
|
category will be used. If no lockstring is specified, default is to let everyone read
|
|
the help file.
|
|
|
|
"""
|
|
key = "@help"
|
|
aliases = "@sethelp"
|
|
locks = "cmd:perm(PlayerHelpers)"
|
|
help_category = "Building"
|
|
|
|
def func(self):
|
|
"Implement the function"
|
|
|
|
switches = self.switches
|
|
lhslist = self.lhslist
|
|
|
|
if not self.args:
|
|
self.msg("Usage: @sethelp/[add|del|append|merge] <topic>[,category[,locks,..] = <text>")
|
|
return
|
|
|
|
topicstr = ""
|
|
category = "General"
|
|
lockstring = "view:all()"
|
|
try:
|
|
topicstr = lhslist[0]
|
|
category = lhslist[1]
|
|
lockstring = ",".join(lhslist[2:])
|
|
except Exception:
|
|
pass
|
|
|
|
if not topicstr:
|
|
self.msg("You have to define a topic!")
|
|
return
|
|
# check if we have an old entry with the same name
|
|
try:
|
|
old_entry = HelpEntry.objects.get(db_key__iexact=topicstr)
|
|
except Exception:
|
|
old_entry = None
|
|
|
|
if 'append' in switches or "merge" in switches:
|
|
# merge/append operations
|
|
if not old_entry:
|
|
self.msg("Could not find topic '%s'. You must give an exact name." % topicstr)
|
|
return
|
|
if not self.rhs:
|
|
self.msg("You must supply text to append/merge.")
|
|
return
|
|
if 'merge' in switches:
|
|
old_entry.entrytext += " " + self.rhs
|
|
else:
|
|
old_entry.entrytext += "\n\n%s" % self.rhs
|
|
self.msg("Entry updated:\n%s" % old_entry.entrytext)
|
|
return
|
|
if 'delete' in switches or 'del' in switches:
|
|
# delete the help entry
|
|
if not old_entry:
|
|
self.msg("Could not find topic '%s'" % topicstr)
|
|
return
|
|
old_entry.delete()
|
|
self.msg("Deleted help entry '%s'." % topicstr)
|
|
return
|
|
|
|
# at this point it means we want to add a new help entry.
|
|
if not self.rhs:
|
|
self.msg("You must supply a help text to add.")
|
|
return
|
|
if old_entry:
|
|
if 'for' in switches or 'force' in switches:
|
|
# overwrite old entry
|
|
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()
|
|
self.msg("Overwrote the old topic '%s' with a new one." % topicstr)
|
|
else:
|
|
self.msg("Topic '%s' already exists. Use /force to overwrite or /append or /merge to add text to it." % topicstr)
|
|
else:
|
|
# no old entry. Create a new one.
|
|
new_entry = create.create_help_entry(topicstr,
|
|
self.rhs, category, lockstring)
|
|
if new_entry:
|
|
self.msg("Topic '%s' was successfully created." % topicstr)
|
|
else:
|
|
self.msg("Error when creating topic '%s'! Maybe it already exists?" % topicstr)
|