diff --git a/yaboli/basic_types.py b/yaboli/basic_types.py index 5ba0110..8f31a69 100644 --- a/yaboli/basic_types.py +++ b/yaboli/basic_types.py @@ -1,5 +1,8 @@ import time +def mention(name): + return "".join(c for c in name if not c in ".!?;&<'\"" and not c.isspace()) + class SessionView(): """ This class keeps track of session details. diff --git a/yaboli/connection.py b/yaboli/connection.py index 71f1029..542aea4 100644 --- a/yaboli/connection.py +++ b/yaboli/connection.py @@ -11,7 +11,9 @@ from .callbacks import Callbacks SSLOPT = {"ca_certs": ssl.get_default_verify_paths().cafile} #SSLOPT = {"cert_reqs": ssl.CERT_NONE} ROOM_FORMAT = "wss://euphoria.io/room/{}/ws" + logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) class Connection(): """ diff --git a/yaboli/session.py b/yaboli/session.py index 09ef470..4d23e59 100644 --- a/yaboli/session.py +++ b/yaboli/session.py @@ -2,7 +2,7 @@ import logging from .callbacks import Callbacks from .connection import Connection -from .basic_types import Message, SessionView +from .basic_types import Message, SessionView, mention logger = logging.getLogger(__name__) @@ -14,19 +14,33 @@ class Session(): - seeing other clients - sending and receiving messages - Events: - enter - can view the room - ready - can view the room and post messages (has a nick) + event (args) | meaning + -------------------|------------------------------------------------- + enter | can view the room + ready | can view the room and post messages (has a nick) + sessions-update | self.sessions has changed + own-session-update | your own message has changed + message (msg) | a message has been received (no own messages) + own-message (msg) | a message that you have sent """ def __init__(self, room, password=None, name=None): self._connection = Connection(room) self._connection.subscribe("disconnect", self._reset_variables) + self._connection.subscribe("bounce-event", self.handle_bounce_event) self._connection.subscribe("disconnect-event", self.handle_disconnect_event) self._connection.subscribe("hello-event", self.handle_hello_event) + self._connection.subscribe("join-event", self.handle_join_event) + self._connection.subscribe("logout-event", self.handle_logout_event) + self._connection.subscribe("network-event", self.handle_network_event) + self._connection.subscribe("nick-event", self.handle_nick_event) + self._connection.subscribe("edit-message-event", self.handle_edit_message_event) + self._connection.subscribe("part-event", self.handle_part_event) self._connection.subscribe("ping-event", self.handle_ping_event) + self._connection.subscribe("pm-initiate-event", self.handle_pm_initiate_event) + self._connection.subscribe("send-event", self.handle_send_event) self._connection.subscribe("snapshot-event", self.handle_snapshot_event) self._callbacks = Callbacks() @@ -58,7 +72,7 @@ class Session(): def _set_name(self, new_name): with self._connection as conn: - logger.debug("setting name to {!r}".format(new_name)) + logger.debug("Setting name to {!r}".format(new_name)) conn.subscribe_to_next(self.handle_nick_reply) conn.send_packet("nick", name=new_name) @@ -80,6 +94,13 @@ class Session(): logger.debug("Adding callback {} to {}".format(callback, event)) self._callbacks.add(event, callback, *args, **kwargs) + def send(self, content, parent=None): + if self._ready: + self._connection.send_packet("send", content=content, parent=parent) + logger.debug("Message sent.") + else: + logger.warn("Attempted to send message while not ready.") + @property def name(self): return self.my_session.name @@ -91,6 +112,10 @@ class Session(): if not self._ready: self._set_name(new_name) + def refresh_sessions(self): + logger.debug("Refreshing sessions") + self._connection.send_packet("who") + def handle_bounce_event(self, data, packet): if data.get("reason") == "authentication required": if self.password: @@ -106,6 +131,7 @@ class Session(): def handle_hello_event(self, data, packet): self.my_session.read_data(data.get("session")) + self._callbacks.call("own-session-update") self.room_is_private = data.get("room_is_private") self.server_version = data.get("version") @@ -114,24 +140,107 @@ class Session(): if self._snapshot_event_completed: self._callbacks.call("enter") + def handle_join_event(self, data, packet): + view = SessionView.from_data(data) + self.sessions[view.session_id] = view + + if view.name: + logger.debug("@{} joined the room.".format(mention(view.name))) + else: + logger.debug("Someone joined the room.") + + self._callbacks.call("sessions-update") + + def handle_logout_event(self, data, packet): + # no idea why this should happen to the bot + # just reconnect, in case it does happen + self._connection.disconnect() + + def handle_network_event(self, data, packet): + if data.get("type") == "partition": + prev_len = len(self.sessions) + + # only remove views matching the server_id/server_era combo + self.sessions = { + sid: view for sid, view in self.sessions.items() + if view.server_id != data.get("server_id") + or view.server_era != data.get("server_era") + } + + if len(sessions) != prev_len: + logger.info("Some people left after a network event.") + else: + logger.info("No people left after a network event.") + + self._callbacks.call("sessions-update") + + def handle_nick_event(self, data, packet): + session_id = data.get("session_id") + + if session_id not in self.sessions: + logger.warn("SessionView not found: Refreshing sessions.") + self.refresh_sessions() + else: + self.sessions[session_id].name = data.get("to") + + if data.get("from"): + logger.debug("@{} changed their name to @{}.".format( + mention(data.get("from")), + mention(data.get("to")) + )) + else: + logger.debug("Someone changed their name to @{}.".format( + mention(data.get("to")) + )) + + self._callbacks.call("sessions-update") + + def handle_edit_message_event(self, data, packet): + # TODO: implement + pass + + def handle_part_event(self, data, packet): + view = SessionView.from_data(data) + if view.session_id not in self.sessions: + logger.warn("SessionView not found: Refreshing sessions.") + self.refresh_sessions() + else: + del self.sessions[view.session_id] + + if view.name: + logger.debug("@{} left the room.".format(mention(view.name))) + else: + logger.debug("Someone left the room.") + + self._callbacks.call("sessions-update") + def handle_ping_event(self, data, packet): with self._connection as conn: - logger.debug("playing ping pong") conn.send_packet("ping-reply", time=data.get("time")) + def handle_pm_initiate_event(self, data, error): + pass # placeholder, maybe implemented in the future + + def handle_send_event(self, data, error): + # TODO: implement + msg = Message.from_data(data) + self._callbacks.call("message", msg) + def handle_snapshot_event(self, data, packet): # deal with connected sessions for item in data.get("listing"): view = SessionView.from_data(item) self.sessions[view.session_id] = view + self._callbacks.call("sessions-update") # deal with messages - # TODO: this + # TODO: implement # deal with other info self.server_version = data.get("version") if "nick" in data: self.my_session.name = data.get("nick") + self._callbacks.call("own-session-update") self._snapshot_event_completed = True if self._hello_event_completed: @@ -145,16 +254,36 @@ class Session(): else: logger.debug("Authetication complete, password was correct.") + def handle_get_message_reply(self, data, packet): + # TODO: implement + pass + + def handle_log_event(self, data, packet): + # TODO: implement + pass + def handle_nick_reply(self, data, packet): first_name = not self.name - if first_name: - logger.info("Changed name to {!r}.".format(data.get("to"))) - else: + if data.get("from"): logger.info("Changed name from {!r} to {!r}.".format(data.get("from"), data.get("to"))) + else: + logger.info("Changed name to {!r}.".format(data.get("to"))) self.my_session.name = data.get("to") + self._callbacks.call("own-session-update") if first_name: self._ready = True self._callbacks.call("ready") + + def handle_send_reply(self, data, packet): + # TODO: implement + msg = Message.from_data(data) + self._callbacks.call("own-message", msg) + + def handle_who_reply(self, data, packet): + for item in data.get("listing"): + view = SessionView.from_data(item) + self.sessions[view.session_id] = view + self._callbacks.call("sessions-update")