diff --git a/evennia/server/inputfuncs.py b/evennia/server/inputfuncs.py
index 4c2750efeb..02611530e0 100644
--- a/evennia/server/inputfuncs.py
+++ b/evennia/server/inputfuncs.py
@@ -25,11 +25,13 @@ from codecs import lookup as codecs_lookup
from django.conf import settings
-from evennia import ObjectDB
+from evennia import DefaultCharacter, TASK_HANDLER
from evennia.commands.cmdhandler import cmdhandler
from evennia.commands.default.general import CmdSay
+from evennia.utils.ansi import strip_mxp
from evennia.utils.logger import log_err
-from evennia.utils.utils import to_str
+from evennia.utils.utils import to_str, delay
+from evennia.scripts.tickerhandler import TICKER_HANDLER
BrowserSessionStore = importlib.import_module(settings.SESSION_ENGINE).SessionStore
@@ -44,6 +46,8 @@ _SA = object.__setattr__
_STRIP_INCOMING_MXP = settings.MXP_ENABLED and settings.MXP_OUTGOING_ONLY
_STRIP_MXP = None
+_IS_TYPING_PARTICIPANTS = {}
+
def _NA(o):
return "N/A"
@@ -142,8 +146,10 @@ def echo(session, *args, **kwargs):
"""
Echo test function
"""
- if _STRIP_INCOMING_MXP:
- txt = strip_mxp(txt)
+ # txt = kwargs.get("txt")
+ #
+ # if _STRIP_INCOMING_MXP and txt:
+ # txt = strip_mxp(txt)
session.data_out(text="Echo returns: %s" % args)
@@ -385,8 +391,6 @@ def repeat(session, *args, **kwargs):
the above settings.
"""
- from evennia.scripts.tickerhandler import TICKER_HANDLER
-
name = kwargs.get("callback", "")
interval = max(5, int(kwargs.get("interval", 60)))
@@ -655,24 +659,104 @@ def msdp_send(session, *args, **kwargs):
session.msg(send=((), out))
+def is_typing_send_update():
+ """
+ Send relevant updates to participants
+
+ """
+ participants = list(_IS_TYPING_PARTICIPANTS.keys())
+
+ for participant in participants:
+ if _IS_TYPING_PARTICIPANTS[participant]["session"].puppet is not None:
+
+ payload = []
+ # Get potentials
+ # TODO: exclude the speaking character
+ potentials = DefaultCharacter.objects.filter_family(db_location=_IS_TYPING_PARTICIPANTS[participant]["session"].puppet.location)
+
+ # if len(potentials) > 0:
+ for puppet in potentials:
+
+ # We're only interested in sending updates if they're capable of receiving them
+ if str(puppet.sessid) in participants:
+ payload.append({
+ "name": puppet.name,
+ "state": _IS_TYPING_PARTICIPANTS[str(puppet.sessid)]['state'],
+ })
+
+ _IS_TYPING_PARTICIPANTS[participant]['session'].msg(is_typing={
+ 'type': 'typing',
+ 'payload': payload
+ })
+ delay(5, is_typing_send_update)
+ else:
+ del _IS_TYPING_PARTICIPANTS[str(participant)]
+
+ if len(_IS_TYPING_PARTICIPANTS.keys()) > 0:
+ delay(5, is_typing_send_update)
+
+
def is_typing_get_aliases(session, *args, **kwargs):
+ """
+ Used in setting up clients. Fetch list of possible "talking" triggers
+
+ Args:
+ session:
+ *args:
+ **kwargs:
+
+ Returns:
+
+ """
+ # Add the participant to the list.
+ _IS_TYPING_PARTICIPANTS[str(session.sessid)] = {
+ "state": False,
+ "session": session,
+ }
+
session.msg(is_typing={'type': 'aliases', 'payload': CmdSay.aliases})
+ if len(_IS_TYPING_PARTICIPANTS.keys()) == 1:
+ delay(5, is_typing_send_update)
-def is_typing_state(session, *args, **kwargs):
- # audience = ObjectDB.objects.filter(db_typeclass_path="typeclasses.characters.Character",
- # db_location=session.puppet.location).exclude(db_key=session.puppet.key)
- audience = ObjectDB.objects.filter(db_typeclass_path="typeclasses.characters.Character",
- db_location=session.puppet.location)
+def is_typing_update_participant(session, *args, **kwargs):
+ """
+ Update a participant session's typing status
- for puppet in audience:
- for puppet_session in puppet.sessions.all():
- puppet_session.msg(is_typing={'type': 'typing',
- 'payload': {
- 'name': session.puppet.name,
- 'state': args[0]
- }})
+ Args:
+ session:
+ *args: First argument is a boolean indicating their typing state
+ **kwargs:
+
+ Returns:
+
+ """
+ # If the session isn't found then server restarted
+ if _IS_TYPING_PARTICIPANTS.get(session.sessid) is None:
+ _IS_TYPING_PARTICIPANTS[str(session.sessid)] = {
+ "session": session,
+ "state": args[0],
+ }
+
+ if len(_IS_TYPING_PARTICIPANTS.keys()) == 1:
+ delay(5, is_typing_send_update)
+ else:
+ _IS_TYPING_PARTICIPANTS[str(session.sessid)]['state'] = args[0]
+
+def is_typing_remove_participant(session, *args, **kwargs):
+ """
+ Handle logging out/ending a session
+
+ Args:
+ session:
+ *args:
+ **kwargs:
+
+ Returns:
+
+ """
+ del _IS_TYPING_PARTICIPANTS[str(session.sessid)]
# client specific
diff --git a/evennia/web/static/webclient/js/plugins/is_typing.js b/evennia/web/static/webclient/js/plugins/is_typing.js
index 0bc857d879..d037546e30 100644
--- a/evennia/web/static/webclient/js/plugins/is_typing.js
+++ b/evennia/web/static/webclient/js/plugins/is_typing.js
@@ -1,209 +1,224 @@
-let is_typing = (function (){
- let Evennia;
- // 10 second timeout
- const timeout = 10 * 1000
- const state = {
- timeout: null,
- callback: null,
- is_typing: false,
- typing_players: [],
- cleanup_callback: null,
+let is_typing = (function () {
+ let Evennia;
+ const timeout = 2 * 1000;
+ const state = {
+ timeout: null,
+ callback: null,
+ is_typing: false,
+ typing_players: [],
+ cleanup_callback: null,
+ };
+
+ const sayCommands = ["say"];
+
+ const createDialog = function () {
+ const ele = [
+ '
",
+ ].join("\n");
+
+ $("body").append(ele);
+ };
+
+ const playerElement = (name) =>
+ `${name} is typing...
`;
+
+ const startedTyping = function () {
+ state.is_typing = true;
+ state.timeout = Date.now() + timeout;
+ state.callback = setTimeout(stoppedTyping, timeout);
+
+ sendIsTyping();
+ };
+
+ const stillTyping = function () {
+ state.timeout = Date.now() + timeout;
+ clearTimeout(state.callback);
+ state.callback = setTimeout(stoppedTyping, timeout);
+ };
+
+ const stoppedTyping = function () {
+ state.is_typing = false;
+ clearTimeout(state.callback);
+ state.callback = null;
+ state.timeout = null;
+
+ sendIsTyping();
+ };
+
+ // Make our commands array regex safe
+ const escapeRegExp = function (text) {
+ return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ };
+
+ // Get the say command's aliases
+ const requestSayAliases = function () {
+ Evennia.msg("is_typing_get_aliases");
+ };
+
+ const setSayAliases = function (aliases) {
+ aliases.forEach((alias) => sayCommands.push(escapeRegExp(alias)));
+ };
+
+ // Update server
+ const sendIsTyping = function () {
+ Evennia.msg("is_typing_update_participant", [state.is_typing]);
+ };
+
+ const onLoggedIn = function () {
+ requestSayAliases();
+ };
+
+ // Listen for talk commands
+ const onKeydown = function (event) {
+ const regex = new RegExp(
+ `^\W*(${sayCommands.reduce((acc, cur) => acc + "|" + cur, "").substring(1)})`,
+ );
+ const inputfield = $(".inputfield:focus");
+
+ // A 'say' command is being used.
+ if (
+ Evennia.isConnected() &&
+ inputfield.length === 1 &&
+ event.key.length === 1 &&
+ inputfield.val().match(regex)
+ ) {
+ if (event.which === 13) {
+ // Enter. Message sent. Reset.
+ stoppedTyping();
+ } else if (!state.is_typing) {
+ // Speaking just started. Set is_talking and timeout.
+ startedTyping();
+ } else if (Date.now() > state.timeout) {
+ // Expiration is nearing. Update timeout.
+ stillTyping();
+ }
+ // Not talking anymore but state hasn't been updated yet.
+ } else if (state.is_typing) {
+ stoppedTyping();
+ }
+ };
+
+ // Reset everything
+ const onConnectionClose = function () {
+ state.is_typing = false;
+ state.timeout = null;
+ state.typing_players = [];
+
+ if (state.callback) {
+ clearTimeout(state.callback);
}
- const sayCommands = ['say']
-
- const createDialog = function() {
- const ele =[
- ''
- ].join('\n')
-
- $('body').append(ele)
+ if (state.cleanup_callback) {
+ clearInterval(state.cleanup_callback);
}
- const playerElement =(name)=> `${name} is typing...
`
+ Evennia.msg("is_typing_remove_participant");
+ };
- const startedTyping = function () {
- state.is_typing = true;
- state.timeout = Date.now() + timeout;
- state.callback = setTimeout(stoppedTyping, timeout);
+ const cleanupTimedOutPlayers = function () {
+ const now = Date.now();
+ const timedOut = [];
- sendIsTyping()
+ state.typing_players.forEach((player, index) => {
+ if (player.timeout < now) {
+ timedOut.push(index);
+ $(`#istyping-${player}`).remove();
+ }
+ });
+
+ timedOut
+ .reverse()
+ .forEach((index) => state.typing_players.splice(index, 1));
+
+ if (state.typing_players.length === 0) {
+ clearInterval(state.cleanup_callback);
+ $("#istyping").hide();
}
+ };
- const stillTyping = function () {
- state.timeout = Date.now() + timeout
- clearTimeout(state.callback)
- state.callback = setTimeout(stoppedTyping, timeout)
+ const is_typing = function (args, kwargs) {
+ if ("type" in kwargs) {
+ switch (kwargs.type) {
+ case "aliases":
+ setSayAliases(kwargs.payload);
+ break;
- sendIsTyping()
- }
+ case "typing":
+ const updated_players = kwargs.payload;
+ let player = null;
- const stoppedTyping = function () {
- state.is_typing = false;
- clearTimeout(state.callback);
- state.callback = null;
- state.timeout = null;
+ updated_players.forEach((updated) => {
+ player = state.typing_players.filter(
+ (player) => player.name === updated.name,
+ );
- sendIsTyping()
- }
+ // New talker
+ if (updated.state && player.length === 0) {
+ state.typing_players.push({
+ name: updated.name,
+ timeout: Date.now() + timeout,
+ });
+ $("#typingplayers").append(playerElement(updated.name));
- // Make our commands array regex safe
- const escapeRegExp = function (text) {
- return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
- }
-
- // Get the say command's aliases
- const requestSayAliases = function () {
- Evennia.msg('is_typing_get_aliases')
- }
-
- const setSayAliases = function (aliases) {
- aliases.forEach(alias=>sayCommands.push(escapeRegExp(alias)))
- }
-
- // Update server
- const sendIsTyping = function () {
- Evennia.msg('is_typing_state', [state.is_typing])
- }
-
- const onLoggedIn = function () {
- requestSayAliases();
- }
-
- // Listen for talk commands
- const onKeydown = function (event) {
- const regex = new RegExp(`^\W*(${sayCommands.reduce((acc, cur)=> acc + "|" + cur, "").substring(1)})`)
- const inputfield = $(".inputfield:focus");
-
- // A 'say' command is being used.
- if (Evennia.isConnected() &&
- inputfield.length === 1 &&
- event.key.length === 1 &&
- inputfield.val().match(regex)) {
- // Enter. Message sent. Reset.
- if (event.which === 13) {
- stoppedTyping()
-
- // Speaking just started. Set is_talking and timeout.
- } else if (!state.is_typing) {
- startedTyping();
-
- // Expiration is nearing. Update timeout.
- } else if (Date.now() + 5 * 1000 > state.timeout) {
- stillTyping();
+ // Existing talker is still going
+ } else if (updated.state && player.length > 0) {
+ player[0].timeout = Date.now() + timeout;
+ // They're done talking
+ } else {
+ state.typing_players = state.typing_players.filter(
+ (player) => player.name !== updated.name,
+ );
+ $(`#istyping-${updated.name}`).remove();
}
- // Not talking anymore but state hasn't been updated yet.
- } else if (state.is_typing) {
- stoppedTyping();
- }
+ });
+
+ if (state.typing_players.length > 0 && !state.cleanup_callback) {
+ state.cleanup_callback = setInterval(cleanupTimedOutPlayers, 100);
+ $("#istyping").show();
+ } else if (
+ state.typing_players.length === 0 &&
+ state.cleanup_callback
+ ) {
+ clearInterval(state.cleanup_callback);
+ state.cleanup_callback = null;
+ $("#istyping").hide();
+ }
+ break;
+
+ default:
+ console.log("Default case");
+ console.log(args);
+ console.log(kwargs);
+ }
}
+ };
- // Reset everything
- const onConnectionClose = function () {
- state.is_typing = false;
- state.timeout = null;
- state.typing_players = []
+ const getState = () => state;
- if (state.callback) {
- clearTimeout(state.callback)
- }
+ //
+ // Mandatory plugin init function
+ const init = function () {
+ let options = window.options;
+ options["is_typing"] = true;
+ Evennia = window.Evennia;
- if (state.cleanup_callback) {
- clearInterval(state.cleanup_callback)
- }
- }
+ Evennia.emitter.on("is_typing", is_typing);
- const cleanupTimedOutPlayers = function () {
- const now = Date.now();
- const timedOut = []
+ createDialog();
- state.typing_players.forEach((player, index)=>{
- if (player.timeout < now) {
- timedOut.push(index)
- $(`#istyping-${player}`).remove()
- }
- })
+ console.log("Is Typing plugin initialized");
+ };
- timedOut.reverse().forEach(index=>state.typing_players.splice(index, 1))
+ return {
+ init,
+ onLoggedIn,
+ onKeydown,
+ onConnectionClose,
+ getState,
+ };
+})();
- if (state.typing_players.length === 0) {
- clearInterval(state.cleanup_callback)
- $('#istyping').hide();
- }
- }
-
- const is_typing = function (args, kwargs) {
- if ('type' in kwargs) {
- switch (kwargs.type) {
- case 'aliases':
- setSayAliases(kwargs.payload)
- break;
-
- case 'typing':
- const player = state.typing_players.filter(player=>player.name === kwargs.payload.name)
-
- // New talker
- if (kwargs.payload.state &&
- player.length === 0) {
- state.typing_players.push({name: kwargs.payload.name, timeout: Date.now() + timeout})
- $('#typingplayers').append(playerElement(kwargs.payload.name))
-
- // Existing talker is still going
- } else if (kwargs.payload.state &&
- player.length > 0) {
- player[0].timeout = Date.now() + timeout;
-
- // They're done talking
- } else {
- state.typing_players = state.typing_players.filter(player=>player.name!== kwargs.payload.name)
- $(`#istyping-${kwargs.payload.name}`).remove()
- }
-
- if (state.typing_players.length > 0 && !state.cleanup_callback) {
- state.cleanup_callback = setInterval(cleanupTimedOutPlayers, 100);
- $('#istyping').show();
-
- } else if (state.typing_players.length === 0 && state.cleanup_callback) {
- clearInterval(state.cleanup_callback)
- state.cleanup_callback = null;
- $('#istyping').hide();
- }
- break;
-
- default:
- console.log("Default case")
- console.log(args)
- console.log(kwargs)
- }
- }
- }
-
- const getState = () => state
-
- //
- // Mandatory plugin init function
- const init = function () {
- let options = window.options;
- options["is_typing"] = true;
- Evennia = window.Evennia;
-
- Evennia.emitter.on("is_typing", is_typing);
-
- createDialog();
-
- console.log('Is Typing plugin initialized');
- }
-
- return {
- init,
- onLoggedIn,
- onKeydown,
- onConnectionClose,
- getState
- }
-})()
-
-window.plugin_handler.add("is_typing", is_typing)
\ No newline at end of file
+window.plugin_handler.add("is_typing", is_typing);