Module userland.scripts.chat
Node chat script
Global variables
var LIMIT
-
Total number of messages to keep in backlog
var MAX_LENGTH
-
Maximum length of individual messages
Functions
async def main(cx: SSHContext) ‑> None
Classes
class ChatApp (context: SSHContext, **kwargs)
-
Node chat Textual app
Create an instance of an app.
Args
driver_class
- Driver class or
None
to auto-detect. This will be used by some Textual tools. css_path
- Path to CSS or
None
to use theCSS_PATH
class variable. To load multiple CSS files, pass a list of strings or paths which will be loaded in order. watch_css
- Reload CSS if the files changed. This is set automatically if
you are using
textual run
with thedev
switch.
Raises
CssPathError
- When the supplied CSS path(s) are an unexpected type.
Expand source code
class ChatApp(XthuluApp): """Node chat Textual app""" BINDINGS = [Binding("escape", "quit", show=False)] redis: Redis """Redis connection""" pubsub: PubSub """Redis PubSub connection""" _log: deque[ChatMessage] _exit_event: Event def __init__(self, context: SSHContext, **kwargs): super().__init__(context, **kwargs) self.redis = Resources().cache self.pubsub = self.redis.pubsub() self.pubsub.subscribe(**{"chat": self.on_chat}) self._log = deque(maxlen=LIMIT) self._exit_event = Event() self.run_worker(self._listen, exclusive=True, thread=True) def _listen(self) -> None: self.redis.publish( "chat", ChatMessage( user=None, message=f"{self.context.username} has joined" ).model_dump_json(), ) while not self._exit_event.is_set(): self.pubsub.get_message(True, 0.01) def compose(self): # chat log yield VerticalScroll(Static(id="log")) # input input_widget = Input( placeholder="Enter a message or press ESC", max_length=MAX_LENGTH, ) input_widget.focus() yield input_widget def on_chat(self, message: dict[str, str]) -> None: def format_message(msg: ChatMessage): if msg.user: return ( f"\n[bright_white on blue]<{msg.user}>[/] " f"{escape(msg.message)}" ) return ( "\n[bright_white on red]<*>[/] " f"[italic][white]{msg.message}[/][/]" ) msg = ChatMessage(**json.loads(message["data"])) self._log.append(msg) l: Static = self.get_widget_by_id("log") # type: ignore l.update( self.console.render_str( "".join([format_message(m) for m in self._log]) ) ) vs = self.query_one(VerticalScroll) vs.scroll_end(animate=False) input = self.query_one(Input) input.value = "" def exit(self) -> None: msg = ChatMessage( user=None, message=f"{self.context.username} has left" ) self.redis.publish("chat", msg.model_dump_json()) self._exit_event.set() self.workers.cancel_all() super().exit() def on_input_submitted(self, event: Input.Submitted) -> None: val = event.input.value.strip() if val == "": return self.redis.publish( "chat", ChatMessage( user=self.context.username, message=val ).model_dump_json(), )
Ancestors
- XthuluApp
- textual.app.App
- typing.Generic
- textual.dom.DOMNode
- textual.message_pump.MessagePump
Class variables
var BINDINGS
var pubsub : redis.client.PubSub
-
Redis PubSub connection
var redis : redis.client.Redis
-
Redis connection
Methods
def compose(self)
-
Yield child widgets for a container.
This method should be implemented in a subclass.
def on_chat(self, message: dict[str, str]) ‑> None
def on_input_submitted(self, event: textual.widgets._input.Input.Submitted) ‑> None
Inherited members
class ChatMessage (**data: Any)
-
Usage docs: https://docs.pydantic.dev/2.7/concepts/models/
A base class for creating Pydantic models.
Attributes
__class_vars__
- The names of classvars defined on the model.
__private_attributes__
- Metadata about the private attributes of the model.
__signature__
- The signature for instantiating the model.
__pydantic_complete__
- Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__
- The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
__pydantic_custom_init__
- Whether the model has a custom
__init__
function. __pydantic_decorators__
- Metadata containing the decorators defined on the model.
This replaces
Model.__validators__
andModel.__root_validators__
from Pydantic V1. __pydantic_generic_metadata__
- Metadata for generic models; contains data used for a similar purpose to args, origin, parameters in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__
- Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__
- The name of the post-init method for the model, if defined.
__pydantic_root_model__
- Whether the model is a
RootModel
. __pydantic_serializer__
- The pydantic-core SchemaSerializer used to dump instances of the model.
__pydantic_validator__
- The pydantic-core SchemaValidator used to validate instances of the model.
__pydantic_extra__
- An instance attribute with the values of extra fields from validation when
model_config['extra'] == 'allow'
. __pydantic_fields_set__
- An instance attribute with the names of fields explicitly set.
__pydantic_private__
- Instance attribute with the values of private attributes set on the model instance.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError
][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.self
is explicitly positional-only to allowself
as a field name.Expand source code
class ChatMessage(BaseModel): user: str | None message: str
Ancestors
- pydantic.main.BaseModel
Class variables
var message : str
var model_computed_fields
var model_config
var model_fields
var user : str | None