diff --git a/yaboli/TestBot.py b/yaboli/TestBot.py new file mode 100644 index 0000000..6c470fe --- /dev/null +++ b/yaboli/TestBot.py @@ -0,0 +1,15 @@ +import asyncio +from controller import Bot + + + +class TestBot(Bot): + def __init__(self): + pass + + async def on_connected(self): + await self.room.set_nick("TestBot") + +if __name__ == "__main__": + bot = TestBot() + asyncio.get_event_loop().run_until_complete(bot.run()) diff --git a/yaboli/asynciotest.py b/yaboli/asynciotest.py new file mode 100644 index 0000000..7d619a0 --- /dev/null +++ b/yaboli/asynciotest.py @@ -0,0 +1,25 @@ +import asyncio + +async def create(): + await asyncio.sleep(3.0) + print("(1) create file") + +async def write(): + await asyncio.sleep(1.0) + print("(2) write into file") + +async def close(): + print("(3) close file") + +async def test(): + await create() + await write() + await close() + await asyncio.sleep(2.0) + loop.stop() + +loop = asyncio.get_event_loop() +asyncio.ensure_future(test()) +loop.run_forever() +print("Pending tasks at exit: %s" % asyncio.Task.all_tasks(loop)) +loop.close() diff --git a/yaboli/connection.py b/yaboli/connection.py index 57731b8..5ab703f 100644 --- a/yaboli/connection.py +++ b/yaboli/connection.py @@ -1,229 +1,96 @@ +import logging +logging.basicConfig(level=logging.DEBUG) +import asyncio +asyncio.get_event_loop().set_debug(True) + import json -import time -import threading -import websocket -from websocket import WebSocketException as WSException +import websockets +from websockets import ConnectionClosed -from . import callbacks -class Connection(): - """ - Stays connected to a room in its own thread. - Callback functions are called when a packet is received. - - Callbacks: - - all the message types from api.euphoria.io - These pass the packet data as argument to the called functions. - The other callbacks don't pass any special arguments. - - "connect" - - "disconnect" - - "stop" - """ - - ROOM_FORMAT = "wss://euphoria.io/room/{}/ws" - - def __init__(self, room, url_format=None): - """ - room - name of the room to connect to + +class Connection: + def __init__(self, url, packet_hook, cookie=None): + self.url = url + self.cookie = cookie + self.packet_hook = packet_hook - """ - - self.room = room - - if not url_format: - url_format = self.ROOM_FORMAT - self._url = url_format.format(self.room) - - self._stopping = False + stopped = False self._ws = None - self._thread = None - self._send_id = 0 - self._callbacks = callbacks.Callbacks() - self._id_callbacks = callbacks.Callbacks() + self._pid = 0 + self._pending_responses = {} - def _connect(self, tries=-1, delay=10): - """ - _connect(tries, delay) -> bool + async def run(self): + self._ws = await websockets.connect(self.url) - tries - maximum number of retries - -1 -> retry indefinitely - - Returns True on success, False on failure. - - Connect to the room. - """ - - while tries != 0: - try: - self._ws = websocket.create_connection( - self._url, - enable_multithread=True - ) - - self._callbacks.call("connect") - - return True - except WSException: - if tries > 0: - tries -= 1 - if tries != 0: - time.sleep(delay) - return False - - def disconnect(self): - """ - disconnect() -> None - - Reconnect to the room. - WARNING: To completely disconnect, use stop(). - """ - - if self._ws: - self._ws.close() + try: + while True: + response = await self._ws.recv() + asyncio.ensure_future(self._handle_json(response)) + except websockets.ConnectionClosed: + pass + finally: + await self._ws.close() # just to make sure it's closed self._ws = None - - self._callbacks.call("disconnect") + stopped = True + + for futures in self._pending_responses: + for future in futures: + future.set_error(ConnectionClosed) + future.cancel() - def launch(self): - """ - launch() -> Thread + async def stop(self): + if not stopped and self._ws: + await self._ws.close() + + async def send(ptype, data=None, await_response=True): + if stopped: + raise ConnectionClosed - Connect to the room and spawn a new thread running run. - """ + pid = self._new_pid() + packet["type"] = ptype + packet["data"] = data + packet["id"] = pid - if self._connect(tries=1): - self._thread = threading.Thread(target=self._run, - name="{}-{}".format(self.room, int(time.time()))) - self._thread.start() - return self._thread + if await_response: + wait_for = self._wait_for_response(pid) + await self._ws.send(json.dumps(packet)) + await wait_for + return wait_for.result() else: - self.stop() + await self._ws.send(json.dumps(packet)) - def _run(self): - """ - _run() -> None - - Receive messages. - """ - - while not self._stopping: - try: - self._handle_json(self._ws.recv()) - except (WSException, ConnectionResetError): - if not self._stopping: - self.disconnect() - self._connect() + def _new_pid(self): + self._pid += 1 + return self._pid - def stop(self): - """ - stop() -> None + async def _handle_json(text): + packet = json.loads(text) - Close the connection to the room. - Joins the thread launched by self.launch(). - """ + # Deal with pending responses + pid = packet.get("id") + for future in self._pending_responses.pop(pid, []): + future.set_result(packet) - self._stopping = True - self.disconnect() - - self._callbacks.call("stop") - - if self._thread and self._thread != threading.current_thread(): - self._thread.join() + # Pass packet onto room + await self.packet_hook(packet) - def next_id(self): - """ - next_id() -> id + def _wait_for_response(pid): + future = asyncio.Future() - Returns the id that will be used for the next package. - """ + if pid not in self._pending_responses: + self._pending_responses[pid] = [] + self._pending_responses[pid].append(future) - return str(self._send_id) + return future + +def do_nothing(*args, **kwargs): + pass + +def run(): + conn = Connection("wss://echo.websocket.org", do_nothing) + loop = asyncio.get_event_loop() + #loop.call_later(3, conn.stop) - def add_callback(self, ptype, callback, *args, **kwargs): - """ - add_callback(ptype, callback, *args, **kwargs) -> None - - Add a function to be called when a packet of type ptype is received. - """ - - self._callbacks.add(ptype, callback, *args, **kwargs) - - def add_id_callback(self, pid, callback, *args, **kwargs): - """ - add_id_callback(pid, callback, *args, **kwargs) -> None - - Add a function to be called when a packet with id pid is received. - """ - - self._id_callbacks.add(pid, callback, *args, **kwargs) - - def add_next_callback(self, callback, *args, **kwargs): - """ - add_next_callback(callback, *args, **kwargs) -> None - - Add a function to be called when the answer to the next message sent is received. - """ - - self._id_callbacks.add(self.next_id(), callback, *args, **kwargs) - - def _handle_json(self, data): - """ - handle_json(data) -> None - - Handle incoming 'raw' data. - """ - - packet = json.loads(data) - self._handle_packet(packet) - - def _handle_packet(self, packet): - """ - _handle_packet(ptype, data) -> None - - Handle incoming packets - """ - - if "data" in packet: - data = packet["data"] - else: - data = None - - if "error" in packet: - error = packet["error"] - else: - error = None - - self._callbacks.call(packet["type"], data, error) - - if "id" in packet: - self._id_callbacks.call(packet["id"], data, error) - self._id_callbacks.remove(packet["id"]) - - def _send_json(self, data): - """ - _send_json(data) -> None - - Send 'raw' json. - """ - - if self._ws: - try: - self._ws.send(json.dumps(data)) - except WSException: - self.disconnect() - - def send_packet(self, ptype, **kwargs): - """ - send_packet(ptype, **kwargs) -> None - - Send a formatted packet. - """ - - packet = { - "type": ptype, - "data": kwargs or None, - "id": str(self._send_id) - } - self._send_id += 1 - self._send_json(packet) + loop.run_until_complete(asyncio.ensure_future(conn.run())) diff --git a/yaboli/controller.py b/yaboli/controller.py new file mode 100644 index 0000000..dde19df --- /dev/null +++ b/yaboli/controller.py @@ -0,0 +1,122 @@ +from .room import Room + + + +class Controller: + """ + Callback order: + + on_start - self.room not available + while running: + + on_ping - always possible (until on_disconnected) + on_bounce - self.room only session + on_hello - self.room only session + + on_connected - self.room session and chat room (fully connected) + on_snapshot - self.room session and chat room + - self.room session and chat room + + on_disconnected - self.room not connected to room any more + on_stop - self.room not available + + """ + + def __init__(self, roomname, human=False, cookie=None): + """ + roomname - name of room to connect to + human - whether the human flag should be set on connections + cookie - cookie to use in HTTP request, if any + """ + + self.roomname = roomname + self.human = human + self.cookie = cookie + + self.room = None + self.running = True + + async def run(self): + await self.on_start() + + while self.running: + self.room = Room(self.roomname, self, self.human, self.cookie) + await self.room.run() + self.room = None + + await self.on_end() + + async def stop(self): + if self.running: + self.running = False + + if self.room: + await self.room.stop() + + # room successfully connected + async def on_connected(self): + """ + Client has successfully (re-)joined the room. + + Use: Actions that are meant to happen upon (re-)connecting to a room, + such as resetting the message history. + """ + + pass + + async def on_disconnected(self): + """ + Client has disconnected from the room. + + This is the last time the old self.room can be accessed. + Use: Reconfigure self before next connection. + Need to store information from old room? + """ + + pass + + async def on_bounce(self, reason=None, auth_options=None, agent_id=None, ip=None): + pass + + async def on_disconnect(self, reason): + pass + + async def on_hello(self, user_id, session, room_is_private, version, account=None, account_has_access=None, account_email_verified=None): + pass + + async def on_join(self, session): + pass + + async def on_login(self, account_id): + pass + + async def on_logout(self): + pass + + async def on_network(self, ntype, server_id, server_era): + pass + + async def on_nick(self, session_id, user_id, from, to): + pass + + async def on_edit_message(self, edit_id, message): + pass + + async def on_part(self, session): + pass + + async def on_ping(self, ptime, pnext): + """ + Default implementation, refer to api.euphoria.io + """ + + await self.room.ping_reply(ptime) + + async def on_pm_initiate(self, from, from_nick, from_room, pm_id): + pass + + async def on_send(self, message): + pass + + async def on_snapshot(self, user_id, snapshot_id, version, listing, log, nick=None, pm_with_nick=None, pm_with_user_id=None): + pass diff --git a/yaboli/room.py b/yaboli/room.py index 360e0e4..7d4dc0c 100644 --- a/yaboli/room.py +++ b/yaboli/room.py @@ -1,617 +1,138 @@ -import time +import asyncio +from .connection import Connection -from . import connection -from . import message -from . import messages -from . import session -from . import sessions -from . import callbacks - -class Room(): - """ - Connects to and provides more abstract access to a room on euphoria. +class Room: + ROOM_FORMAT = "wss://euphoria.io/room/{}/ws" + HUMAN_FORMAT = f"{ROOM_FORMAT}?h=1" - callback (values passed) - description - ---------------------------------------------------------------------------------- - delete (message) - message has been deleted - edit (message) - message has been edited - identity - own session or nick has changed - join (session) - user has joined the room - message (message) - message has been sent - messages - message data has changed - nick (session, old, new) - user has changed their nick - part (session) - user has left the room - ping - ping event has happened - room - room info has changed - sessions - session data has changed - change - room has been changed - """ - - def __init__(self, room=None, nick=None, password=None, message_limit=500): - """ - room - name of the room to connect to - nick - nick to assume, None -> no nick - password - room password (in case the room is private) - message_limit - maximum amount of messages that will be stored at a time - None - no limit - """ + def __init__(self, roomname, controller, human=False, cookie=None): + self.roomname = roomname + self.controller = controller + self.human = human + self.cookie = cookie - self.room = room - self.password = password - self.room_is_private = None - self.pm_with_nick = None - self.pm_with_user = None - - self.nick = nick + # Keeps track of sessions, but not messages, since they might be dealt + # with differently by different controllers. + # If you need to keep track of messages, use utils.Log. self.session = None - self.message_limit = message_limit + self.sessions = None - self.ping_last = 0 - self.ping_next = 0 - self.ping_offset = 0 # difference between server and local time + self._callbacks = {} + self._add_callbacks() - self._messages = None - self._sessions = None - - self._callbacks = callbacks.Callbacks() - - self._con = None - - if self.room: - self.change(self.room, password=self.password) - - def launch(self): - """ - launch() -> Thread - - Open connection in a new thread (see connection.Connection.launch). - """ - - return self._con.launch() - - def stop(self): - """ - stop() -> None - - Close connection to room. - """ - - self._con.stop() - - def change(self, room, password=None): - """ - change(room) -> None - - Leave current room (if already connected) and join new room. - Clears all messages and sessions. - A call to launch() is necessary to start a new thread again. - """ - - if self._con: - self._con.stop() - - self.room = room - self.password = password - self.room_is_private = None - self.pm_with_nick = None - self.pm_with_user = None - - self.session = None - - self.ping_last = 0 - self.ping_next = 0 - self.ping_offset = 0 # difference between server and local time - - self._messages = messages.Messages(message_limit=self.message_limit) - self._sessions = sessions.Sessions() - - self._con = connection.Connection(self.room) - - self._con.add_callback("bounce-event", self._handle_bounce_event) - self._con.add_callback("disconnect-event", self._handle_disconnect_event) - self._con.add_callback("hello-event", self._handle_hello_event) - self._con.add_callback("join-event", self._handle_join_event) - self._con.add_callback("network-event", self._handle_network_event) - self._con.add_callback("nick-event", self._handle_nick_event) - self._con.add_callback("edit-message-event", self._handle_edit_message_event) - self._con.add_callback("part-event", self._handle_part_event) - self._con.add_callback("ping-event", self._handle_ping_event) - self._con.add_callback("send-event", self._handle_send_event) - self._con.add_callback("snapshot-event", self._handle_snapshot_event) - - self._callbacks.call("change") - - def add_callback(self, event, callback, *args, **kwargs): - """ - add_callback(ptype, callback, *args, **kwargs) -> None - - Add a function to be called when a certain event happens. - """ - - self._callbacks.add(event, callback, *args, **kwargs) - - def get_msg(self, mid): - """ - get_msg(message_id) -> Message - - Returns the message with the given id, if found. - """ - - return self._messages.get(mid) - - def get_msg_parent(self, mid): - """ - get_msg_parent(message_id) -> Message - - Returns the message's parent, if found. - """ - - return self._messages.get_parent(mid) - - def get_msg_children(self, mid): - """ - get_msg_children(message_id) -> list - - Returns a sorted list of children of the given message, if found. - """ - - return self._messages.get_children(mid) - - def get_msg_top_level(self): - """ - get_msg_top_level() -> list - - Returns a sorted list of top-level messages. - """ - - return self._messages.get_top_level() - - def get_msg_oldest(self): - """ - get_msg_oldest() -> Message - - Returns the oldest message, if found. - """ - - return self._messages.get_oldest() - - def get_msg_youngest(self): - """ - get_msg_youngest() -> Message - - Returns the youngest message, if found. - """ - - return self._messages.get_youngest() - - def get_session(self, sid): - """ - get_session(session_id) -> Session - - Returns the session with that id. - """ - - return self._sessions.get(sid) - - def get_sessions(self): - """ - get_sessions() -> list - - Returns the full list of sessions. - """ - - return self._sessions.get_all() - - def get_people(self): - """ - get_people() -> list - - Returns a list of all non-bot and non-lurker sessions. - """ - - return self._sessions.get_people() - - def get_accounts(self): - """ - get_accounts() -> list - - Returns a list of all logged-in sessions. - """ - - return self._sessions.get_accounts() - - def get_agents(self): - """ - get_agents() -> list - - Returns a list of all sessions who are not signed into an account and not bots or lurkers. - """ - - return self._sessions.get_agents() - - def get_bots(self): - """ - get_bots() -> list - - Returns a list of all bot sessions. - """ - - return self._sessions.get_bots() - - def get_lurkers(self): - """ - get_lurkers() -> list - - Returns a list of all lurker sessions. - """ - - return self._sessions.get_lurkers() - - def set_nick(self, nick): - """ - set_nick(nick) -> None - - Change your nick. - """ - - self.nick = nick - - if not self.session or self.session.name != self.nick: - self._con.add_next_callback(self._handle_nick_reply) - self._con.send_packet("nick", name=nick) - - def mentionable(self, nick=None): - """ - mentionable() - - A mentionable version of the nick. - The nick defaults to the bot's nick. - """ - - if nick is None: - nick = self.nick - - return "".join(c for c in nick if not c in ".!?;&<'\"" and not c.isspace()) - - def send_message(self, content, parent=None): - """ - send_message(content, parent) -> None - - Send a message. - """ - - self._con.add_next_callback(self._handle_send_reply) - self._con.send_packet("send", content=content, parent=parent) - - def authenticate(self, password=None): - """ - authenticate(passsword) -> None - - Try to authenticate so you can enter the room. - """ - - self.password = password - - self._con.add_next_callback(self._handle_auth_reply) - self._con.send_packet("auth", type="passcode", passcode=self.password) - - def update_sessions(self): - """ - update_sessions() -> None - - Resets and then updates the list of sessions. - """ - - self._con.add_next_callback(self._handle_who_reply) - self._con.send_packet("who") - - def load_msgs(self, number=50): - """ - load_msgs(number) -> None - - Request a certain number of older messages from the server. - """ - - self._con.add_next_callback(self._handle_log_reply) - self._con.send_packet("log", n=number, before=self.get_msg_oldest().id) - - def load_msg(self, mid): - """ - load_msg(message_id) -> None - - Request an untruncated version of the message with that id. - """ - - self._con.add_next_callback(self._handle_get_message_reply) - self._con.send_packet("get-message", id=mid) - - # ----- HANDLING OF EVENTS ----- - - def _handle_connect(self): - """ - TODO - """ - - self._callbacks.call("connect") - - def _handle_disconnect(self): - """ - TODO - """ - - self._callbacks.call("disconnect") - - def _handle_stop(self): - """ - TODO - """ - - self._callbacks.call("stop") - - def _handle_bounce_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "bounce-event", error) - self.stop() - return - - if self.password is not None: - self.authenticate(self.password) + if human: + url = HUMAN_FORMAT.format(self.roomname) else: - self.stop() + url = ROOM_FORMAT.format(self.roomname) + self._conn = Connection(url, self._handle_packet, self.cookie) - def _handle_disconnect_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "disconnect-event", error) - self.stop() - return - - self._con.disconnect() + async def run(self): + await self._conn.run() - def _handle_hello_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "hello-event", error) - self.stop() - return - - self.session = session.Session.from_data(data["session"]) - self._sessions.add(self.session) - self._callbacks.call("identity") - self._callbacks.call("sessions") - - self.room_is_private = data["room_is_private"] - self._callbacks.call("room") + async def stop(self): + await self._conn.stop() - def _handle_join_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "join-event", error) - self.update_sessions() - return - - ses = session.Session.from_data(data) - self._sessions.add(ses) - self._callbacks.call("join", ses) - self._callbacks.call("sessions") + # CATEGORY: SESSION COMMANDS - def _handle_network_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "network-event", error) - return - - if data["type"] == "partition": - self._sessions.remove_on_network_partition(data["server_id"], data["server_era"]) - self._callbacks.call("sessions") + async def auth(atype, passcode): + pass # TODO - def _handle_nick_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "nick-event", error) - self.update_sessions() - return - - ses = self.get_session(data["session_id"]) - if ses: - ses.name = data["to"] - self._callbacks.call("nick", ses, data["from"], data["to"]) - self._callbacks.call("sessions") + async def ping_reply(time): + pass # TODO - def _handle_edit_message_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "edit-message-event", error) - return - - msg = message.Message.from_data(data) - if msg: - self._messages.add(msg) - - if msg.deleted: - self._callbacks.call("delete", msg) - elif msg.edited: - self._callbacks.call("edit", msg) - - self._callbacks.call("messages") + # CATEGORY: CHAT ROOM COMMANDS - def _handle_part_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "part-event", error) - self.update_sessions() - return - - ses = session.Session.from_data(data) - if ses: - self._sessions.remove(ses.session_id) - - self._callbacks.call("part", ses) - self._callbacks.call("sessions") + async def get_message(message_id): + pass # TODO - def _handle_ping_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "ping-event", error) - return - - self.ping_last = data["time"] - self.ping_next = data["next"] - self.ping_offset = self.ping_last - time.time() - - self._con.send_packet("ping-reply", time=self.ping_last) - self._callbacks.call("ping") + async def log(n, before=None): + pass # TODO - def _handle_send_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "send-event", error) - return - - msg = message.Message.from_data(data) - self._callbacks.call("message", msg) - - self._messages.add(msg) - self._callbacks.call("messages") + async def nick(name): + pass # TODO - def _handle_snapshot_event(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "snapshot-event", error) - self.stop() - return - - self.set_nick(self.nick) - - if "pm_with_nick" in data or "pm_with_user_id" in data: - if "pm_with_nick" in data: - self.pm_with_nick = data["pm_with_nick"] - if "pm_with_user_id" in data: - self.pm_with_user_id = data["pm_with_user_id"] - self._callbacks.call("room") - - self._sessions.remove_all() - for sesdata in data["listing"]: - self._sessions.add_from_data(sesdata) - self._callbacks.call("sessions") - - self._messages.remove_all() - for msgdata in data["log"]: - self._messages.add_from_data(msgdata) - self._callbacks.call("messages") + async def pm_initiate(user_id): + pass # TODO - # ----- HANDLING OF REPLIES ----- + async def send(content, parent=None): + pass # TODO - def _handle_auth_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "auth-reply", error) - self.stop() - return - - if not data["success"]: - self._con.stop() + async def who() + pass # TODO - def _handle_get_message_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "get-message-reply", error) - return - - self._messages.add_from_data(data) - self._callbacks.call("messages") + # CATEGORY: ACCOUNT COMMANDS + # NYI, and probably never will - def _handle_log_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "log-reply", error) - return - - for msgdata in data["log"]: - self._messages.add_from_data(msgdata) - self._callbacks.call("messages") + # CATEGORY: ROOM HOST COMMANDS + # NYI, and probably never will - def _handle_nick_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "nick-reply", error) - return - - if "to" in data: - self.session.name = self.nick - self._callbacks.call("identity") - - if data["to"] != self.nick: - self.set_nick(self.nick) + # CATEGORY: STAFF COMMANDS + # NYI, and probably never will - def _handle_send_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "send-reply", error) - return - - self._messages.add_from_data(data) - self._callbacks.call("messages") - def _handle_who_reply(self, data, error): - """ - TODO - """ - - if error: - self._callbacks.call("error", "who-reply", error) - return - - self._sessions.remove_all() - for sesdata in data["listing"]: - self._sessions.add_from_data(sesdata) - self._callbacks.call("sessions") + + # All the private functions for dealing with stuff + + def _add_callbacks(self): + self._callbacks["bounce-event"] = self._handle_bounce + self._callbacks["disconnect-event"] = self._handle_disconnect + self._callbacks["hello-event"] = self._handle_hello + self._callbacks["join-event"] = self._handle_join + self._callbacks["login-event"] = self._handle_login + self._callbacks["logout-event"] = self._handle_logout + self._callbacks["network-event"] = self._handle_network + self._callbacks["nick-event"] = self._handle_nick + self._callbacks["edit-message-event"] = self._handle_edit_message + self._callbacks["part-event"] = self._handle_part + self._callbacks["ping-event"] = self._handle_ping + self._callbacks["pm-initiate-event"] = self._handle_pm_initiate + self._callbacks["send-event"] = self._handle_send + self._callbacks["snapshot-event"] = self._handle_snapshot + + async def _handle_packet(self, packet): + ptype = packet.get("type") + callback = self._callbacks.get(ptype) + if callback: + await callback(packet) + + async def _handle_bounce(self, packet): + pass # TODO + + async def _handle_disconnect(self, packet): + pass # TODO + + async def _handle_hello(self, packet): + pass # TODO + + async def _handle_join(self, packet): + pass # TODO + + async def _handle_login(self, packet): + pass # TODO + + async def _handle_logout(self, packet): + pass # TODO + + async def _handle_network(self, packet): + pass # TODO + + async def _handle_nick(self, packet): + pass # TODO + + async def _handle_edit_message(self, packet): + pass # TODO + + async def _handle_part(self, packet): + pass # TODO + + async def _handle_ping(self, packet): + pass # TODO + + async def _handle_pm_initiate(self, packet): + pass # TODO + + async def _handle_send(self, packet): + pass # TODO + + async def _handle_snapshot(self, packet): + pass # TODO