added guards for session.puppet, is_typing module docs, timeout and regex bugs in the client plugin, added client_live_report_typing to Command, and moved class_from_module call to module level

This commit is contained in:
michael
2026-06-23 10:29:36 -07:00
parent 38ba4f9a7b
commit 673e606940
4 changed files with 58 additions and 29 deletions

View File

@ -165,12 +165,16 @@ class Command(metaclass=CommandMeta):
this is usually the same as caller.
self.raw_string - the full raw string input, including the command name,
any args and no parsing.
self.client_live_report_typing - whether the client should report on the typing
status of a user while using a command.
defaults to false.
The following class properties can/should be defined on your child class:
key - identifier for command (e.g. "look")
aliases - (optional) list of aliases (e.g. ["l", "loo"])
locks - lock string (default is "cmd:all()")
help_category - how to organize this help entry in help system
(default is "General")
auto_help - defaults to True. Allows for turning off auto-help generation
@ -205,6 +209,9 @@ class Command(metaclass=CommandMeta):
is_exit = False
# define the command not only by key but by the regex form of its arguments
arg_regex = settings.COMMAND_DEFAULT_ARG_REGEX
# whether this command and its aliases should report on the typing status of the
# user.
client_live_report_typing = False
# whether self.msg sends to all sessions of a related account/object (default
# is to only send to the session sending the command).
msg_all_sessions = settings.COMMAND_DEFAULT_MSG_ALL_SESSIONS

View File

@ -26,7 +26,6 @@ from codecs import lookup as codecs_lookup
from django.conf import settings
from evennia.commands.cmdhandler import cmdhandler
from evennia.commands.default.general import CmdSay
from evennia.utils.logger import log_err
from evennia.utils.utils import to_str

View File

@ -1,6 +1,19 @@
"""
This module allows users based on a given condition (defaults to same location) to see
whether applicable users are typing or not. Currently, only the webclient is supported.
Relevant Settings:
WEBCLIENT_TYPING_TIMEOUT - the timeout in seconds between polling intervals.
WEBCLIENT_TYPING_AUDIENCE_GETTER - the path to the method that returns the sessions
that should receive typing updates. Must return
a list of session objects.
Upon the webclient loading the is_typing plugin, it will request setup from the server
(is_typing_setup) which will return the timeout and what words to watch for. Anytime
the client uses a relevant word, it will notify the server of the relevant session and
its state (typing or not typing). The server then fetches that session's relevant (if
any) other sessions based on the criteria of the audience_getter and passes on the state
update to the fetched audience.
"""
from django.conf import settings
@ -27,6 +40,15 @@ def is_typing_get_audience_common_location(session, *args, **kwargs):
return audience
# A utility to fetch the method used to get the relevant audience for client live
# reporting commands. The retrieved method should return a list of session objects.
# Sessions without a puppet will be ignored.
audience_getter = class_from_module(
settings.WEBCLIENT_TYPING_AUDIENCE_GETTER
or "evennia.server.is_typing.is_typing_get_audience_common_location"
)
def is_typing_setup(session, *args, **kwargs):
"""
This fetches any commands/aliases/nicks that we want to monitor and the
@ -39,7 +61,7 @@ def is_typing_setup(session, *args, **kwargs):
options = session.protocol_flags
is_typing = options.get("ISTYPING", True)
if not is_typing:
if not is_typing or session.puppet is None:
return
live_report_commands = [
@ -67,7 +89,7 @@ def is_typing_setup(session, *args, **kwargs):
)
def is_typing_state(session, *args, **kwargs):
def is_typing_state(user_session, *args, **kwargs):
"""
Broadcasts a typing state update from the session's puppet
to all other characters meeting the configured conditions
@ -78,31 +100,31 @@ def is_typing_state(session, *args, **kwargs):
**kwargs:
- state (bool): The typing state to broadcast.
"""
options = session.protocol_flags
global audience_getter
options = user_session.protocol_flags
is_typing = options.get("ISTYPING", True)
if not is_typing:
if not is_typing or user_session.puppet is None:
return
audience_getter = class_from_module(
settings.WEBCLIENT_TYPING_AUDIENCE_GETTER
or "evennia.server.is_typing.is_typing_get_audience_common_location"
)
state = kwargs.get("state")
audience = audience_getter(session=session, args=args, kwargs=kwargs)
audience = audience_getter(session=user_session, args=args, kwargs=kwargs)
for puppet in audience:
# Filter out clients not interested in updates
relevant_sessions = [
puppet_session
for puppet in audience
for puppet_session in puppet.sessions.all()
if puppet_session.protocol_flags.get("ISTYPING", True)
] # Potential timeout adjustment based on audience size
for puppet_session in puppet.sessions.all():
puppet_session_options = puppet_session.protocol_flags
puppet_session_is_typing = puppet_session_options.get("ISTYPING", True)
if puppet_session_is_typing:
# Update relevant clients
for puppet_session in relevant_sessions:
puppet_session.msg(
is_typing={
"type": "typing",
"payload": {"name": session.puppet.name, "state": state},
"payload": {"name": user_session.puppet.name, "state": state},
}
)

View File

@ -119,7 +119,7 @@ let is_typing = (function (){
// A live report command is being used.
if (Evennia.isConnected() &&
inputfield.length === 1 &&
event.key.length === 1 &&
// event.key.length === 1 &&
inputfield.val().match(regex)) {
// Enter. Message sent. Reset.
if (event.which === 13) {
@ -129,10 +129,9 @@ let is_typing = (function (){
} else if (!state.is_typing) {
startedTyping();
// Expiration is nearing. Update timeout.
} else if (Date.now() + timeout > state.timeout) {
// Expiration is nearing. Update timeout. Default is 5 seconds.
} else if (Date.now() > state.timeout - timeout * .2) {
stillTyping();
}
// Not talking anymore but state hasn't been updated yet.
} else if (state.is_typing) {
@ -198,7 +197,9 @@ let is_typing = (function (){
timeout = typing_timeout
setLiveReportKeywords(live_report_keywords)
regex = new RegExp(`^\W*(${liveReportKeywords.reduce((acc, cur)=> acc + "|" + cur, "").substring(1)})`)
const wordCmds = liveReportKeywords.filter(kw => kw.length > 1).join("|");
const charCmds = liveReportKeywords.filter(kw => kw.length === 1).join("|");
regex = new RegExp(`^\\W*((${wordCmds})(\\s|$)|(${charCmds}))`)
break;
case 'typing':