diff --git a/yaboli/TestBot.py b/yaboli/TestBot.py index 6c470fe..a98f922 100644 --- a/yaboli/TestBot.py +++ b/yaboli/TestBot.py @@ -1,15 +1,21 @@ import asyncio -from controller import Bot +#from controller import Bot +from controller import Controller -class TestBot(Bot): - def __init__(self): - pass +#class TestBot(Bot): +class TestBot(Controller): + def __init__(self, roomname): + super().__init__(roomname) - async def on_connected(self): - await self.room.set_nick("TestBot") + #async def on_connected(self): + #await self.room.set_nick("TestBot") + + async def on_hello(self, user_id, session, room_is_private, version, account=None, + account_has_access=None, account_email_verified=None): + print(repr(session.user_id), repr(session.session_id), repr(session.name)) if __name__ == "__main__": - bot = TestBot() + bot = TestBot("test") asyncio.get_event_loop().run_until_complete(bot.run()) diff --git a/yaboli/connection.py b/yaboli/connection.py index 5ab703f..f5d4b79 100644 --- a/yaboli/connection.py +++ b/yaboli/connection.py @@ -44,7 +44,7 @@ class Connection: if not stopped and self._ws: await self._ws.close() - async def send(ptype, data=None, await_response=True): + async def send(self, ptype, data=None, await_response=True): if stopped: raise ConnectionClosed @@ -65,7 +65,7 @@ class Connection: self._pid += 1 return self._pid - async def _handle_json(text): + async def _handle_json(self, text): packet = json.loads(text) # Deal with pending responses @@ -76,7 +76,7 @@ class Connection: # Pass packet onto room await self.packet_hook(packet) - def _wait_for_response(pid): + def _wait_for_response(self, pid): future = asyncio.Future() if pid not in self._pending_responses: diff --git a/yaboli/controller.py b/yaboli/controller.py index dde19df..d778a41 100644 --- a/yaboli/controller.py +++ b/yaboli/controller.py @@ -1,4 +1,4 @@ -from .room import Room +from room import Room @@ -53,7 +53,12 @@ class Controller: if self.room: await self.room.stop() - # room successfully connected + async def on_start(self): + pass + + async def on_stop(self): + pass + async def on_connected(self): """ Client has successfully (re-)joined the room. @@ -81,7 +86,8 @@ class Controller: 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): + 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): @@ -96,7 +102,7 @@ class Controller: async def on_network(self, ntype, server_id, server_era): pass - async def on_nick(self, session_id, user_id, from, to): + async def on_nick(self, session_id, user_id, from_name, to_name): pass async def on_edit_message(self, edit_id, message): @@ -109,14 +115,15 @@ class Controller: """ 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): + async def on_pm_initiate(self, from_id, 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): + 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 7d4dc0c..ee33641 100644 --- a/yaboli/room.py +++ b/yaboli/room.py @@ -1,5 +1,6 @@ import asyncio -from .connection import Connection +from connection import Connection +import utils class Room: ROOM_FORMAT = "wss://euphoria.io/room/{}/ws" @@ -15,15 +16,22 @@ class Room: # with differently by different controllers. # If you need to keep track of messages, use utils.Log. self.session = None - self.sessions = None + self.account = None + self.listing = utils.Listing() + + # Various room information + self.account_has_access = None + self.account_email_verified = None + self.room_is_private = None + self.version = None # the version of the code being run and served by the server self._callbacks = {} self._add_callbacks() if human: - url = HUMAN_FORMAT.format(self.roomname) + url = self.HUMAN_FORMAT.format(self.roomname) else: - url = ROOM_FORMAT.format(self.roomname) + url = self.ROOM_FORMAT.format(self.roomname) self._conn = Connection(url, self._handle_packet, self.cookie) async def run(self): @@ -34,30 +42,30 @@ class Room: # CATEGORY: SESSION COMMANDS - async def auth(atype, passcode): + async def auth(self, atype, passcode): pass # TODO - async def ping_reply(time): + async def ping_reply(self, time): pass # TODO # CATEGORY: CHAT ROOM COMMANDS - async def get_message(message_id): + async def get_message(self, message_id): pass # TODO - async def log(n, before=None): + async def log(self, n, before=None): pass # TODO - async def nick(name): + async def nick(self, name): pass # TODO - async def pm_initiate(user_id): + async def pm_initiate(self, user_id): pass # TODO - async def send(content, parent=None): + async def send(self, content, parent=None): pass # TODO - async def who() + async def who(self): pass # TODO # CATEGORY: ACCOUNT COMMANDS @@ -102,7 +110,29 @@ class Room: pass # TODO async def _handle_hello(self, packet): - pass # TODO + """ + From api.euphoria.io: + A hello-event is sent by the server to the client when a session is + started. It includes information about the client’s authentication and + associated identity. + """ + + data = packet.get("data") + self.session = utils.Session.from_dict(data.get("session")) + self.account_has_access = data.get("account_has_access") + self.account_email_verified = data.get("account_email_verified") + self.room_is_private = data.get("room_is_private") + self.version = data.get("version") + + await self.controller.on_hello( + data.get("id"), + self.session, + self.room_is_private, + self.version, + account=self.account, + account_has_access=self.account_has_access, + account_email_verified=self.account_email_verified + ) async def _handle_join(self, packet): pass # TODO @@ -126,7 +156,15 @@ class Room: pass # TODO async def _handle_ping(self, packet): - pass # TODO + """ + From api.euphoria.io: + A ping-event represents a server-to-client ping. The client should send + back a ping-reply with the same value for the time field as soon as + possible (or risk disconnection). + """ + + data = packet.get("data") + await self.controller.on_ping(data.get("time"), data.get("next")) async def _handle_pm_initiate(self, packet): pass # TODO diff --git a/yaboli/utils.py b/yaboli/utils.py new file mode 100644 index 0000000..1d7f27f --- /dev/null +++ b/yaboli/utils.py @@ -0,0 +1,66 @@ +class Session: + def __init__(self, user_id, name, server_id, server_era, session_id, is_staff=None, + is_manager=None, client_address=None, real_address=None): + self.user_id = user_id + self.name = name + self.server_id = server_id + self.server_era = server_era + self.session_id = session_id + self.is_staff = is_staff + self.is_manager = is_manager + self.client_address = client_address + self.real_address = real_address + + @classmethod + def from_dict(cls, d): + return cls( + d.get("id"), + d.get("name"), + d.get("server_id"), + d.get("server_era"), + d.get("session_id"), + d.get("is_staff"), + d.get("is_manager"), + d.get("client_address"), + d.get("real_address") + ) + + @property + def client_type(self): + # account, agent or bot + return self.user_id.split(":")[0] + +class Listing: + def __init__(self): + self._sessions = {} + + def __len__(self): + return len(self._sessions) + + def add(self, session): + self._sessions[session.session_id] = session + + def remove(self, session_id): + self._sessions.pop(session_id) + + def by_sid(self, session_id): + return self._sessions.get(session_id); + + def by_uid(self, user_id): + return [ses for ses in self._sessions if ses.user_id == user_id] + + def get_people(self): + return {uid: ses for uid, ses in self._sessions.items() + if ses.client_type in ["agent", "account"]} + + def get_accounts(self): + return {uid: ses for uid, ses in self._sessions.items() + if ses.client_type is "account"} + + def get_agents(self): + return {uid: ses for uid, ses in self._sessions.items() + if ses.client_type is "agent"} + + def get_bots(self): + return {uid: ses for uid, ses in self._sessions.items() + if ses.client_type is "bot"}