From dfad3241fbed0f33446ea83f51e29082f7916f73 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 2 Sep 2017 12:27:38 +0000 Subject: [PATCH] Make more events work (and use an annoying bot to test them) --- yaboli/TestBot.py | 33 ++++++++-- yaboli/connection.py | 20 ++++-- yaboli/controller.py | 2 +- yaboli/room.py | 152 +++++++++++++++++++++++++++++++++++++------ yaboli/utils.py | 42 ++++++++---- 5 files changed, 208 insertions(+), 41 deletions(-) diff --git a/yaboli/TestBot.py b/yaboli/TestBot.py index 6eba07c..5ef772f 100644 --- a/yaboli/TestBot.py +++ b/yaboli/TestBot.py @@ -1,6 +1,7 @@ import asyncio #from controller import Bot from controller import Controller +from utils import * @@ -12,10 +13,34 @@ class TestBot(Controller): async def on_snapshot(self, user_id, session_id, version, listing, log, nick=None, pm_with_nick=None, pm_with_user_id=None): await self.room.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)) + + async def on_send(self, message): + await self.room.send("Hey, a message!", message.message_id) + + async def on_join(self, session): + if session.nick != "": + await self.room.send(f"Hey, a @{mention(session.nick)}!") + else: + await self.room.send("Hey, a lurker!") + + async def on_nick(self, session_id, user_id, from_nick, to_nick): + if from_nick != "" and to_nick != "": + if from_nick == to_nick: + await self.room.send(f"You didn't even change your nick, @{mention(to_nick)} :(") + else: + await self.room.send(f"Bye @{mention(from_nick)}, hi @{mention(to_nick)}") + elif from_nick != "": + await self.room.send(f"Bye @{mention(from_nick)}? This message should never appear...") + elif to_nick != "": + await self.room.send(f"Hey, a @{mention(to_nick)}!") + else: + await self.room.send("I have no idea how you did that. This message should never appear...") + + async def on_part(self, session): + if session.nick != "": + await self.room.send(f"Bye, you @{mention(session.nick)}!") + else: + await self.room.send("Bye, you lurker!") if __name__ == "__main__": bot = TestBot("test") diff --git a/yaboli/connection.py b/yaboli/connection.py index 2e0335b..8488651 100644 --- a/yaboli/connection.py +++ b/yaboli/connection.py @@ -36,8 +36,8 @@ class Connection: stopped = True for future in self._pending_responses: - future.set_error(ConnectionClosed) - future.cancel() # TODO: Is this needed? + #future.set_error(ConnectionClosed) + future.cancel() async def stop(self): if not self.stopped and self._ws: @@ -92,6 +92,16 @@ class Connection: #c = Connection("wss://euphoria.io/room/test/ws", handle_packet) -#def run(): - #loop = asyncio.get_event_loop() - #loop.run_until_complete(asyncio.ensure_future(c.run())) +async def await_future(f): + await f + print(f.result()) + +def run(): + f = asyncio.Future() + #f.set_result("Hello World!") + f.cancel() + #f.set_result("Hello World!") + + loop = asyncio.get_event_loop() + loop.run_until_complete(await_future(f)) + #loop.run_until_complete(c.run()) diff --git a/yaboli/controller.py b/yaboli/controller.py index 031416d..2a98b72 100644 --- a/yaboli/controller.py +++ b/yaboli/controller.py @@ -110,7 +110,7 @@ class Controller: async def on_network(self, ntype, server_id, server_era): pass - async def on_nick(self, session_id, user_id, from_name, to_name): + async def on_nick(self, session_id, user_id, from_nick, to_nick): pass async def on_edit_message(self, edit_id, message): diff --git a/yaboli/room.py b/yaboli/room.py index 63b2aa8..cc7ef4f 100644 --- a/yaboli/room.py +++ b/yaboli/room.py @@ -49,6 +49,7 @@ class Room: async def ping_reply(self, time): """ + From api.euphoria.io: The ping command initiates a client-to-server ping. The server will send back a ping-reply with the same timestamp as soon as possible. @@ -70,6 +71,7 @@ class Room: """ session_id, user_id, from_nick, to_nick = await nick(name) + From api.euphoria.io: The nick command sets the name you present to the room. This name applies to all messages sent during this session, until the nick command is called again. @@ -79,7 +81,9 @@ class Room: """ data = {"name": name} + response = await self._conn.send("nick", data) + self._check_for_errors(response) session_id = response.get("session_id") user_id = response.get("id") @@ -94,7 +98,28 @@ class Room: pass # TODO async def send(self, content, parent=None): - pass # TODO + """ + From api.euphoria.io: + The send command sends a message to a room. The session must be + successfully joined with the room. This message will be broadcast to + all sessions joined with the room. + + If the room is private, then the message content will be encrypted + before it is stored and broadcast to the rest of the room. + + The caller of this command will not receive the corresponding + send-event, but will receive the same information in the send-reply. + """ + + data = {"content": content} + if parent: + data["parent"] = parent + + response = await self._conn.send("send", data) + self._check_for_errors(response) + + message = utils.Message.from_dict(response.get("data")) + return message async def who(self): pass # TODO @@ -129,16 +154,52 @@ class Room: self._callbacks["snapshot-event"] = self._handle_snapshot async def _handle_packet(self, packet): + self._check_for_errors(packet) + ptype = packet.get("type") callback = self._callbacks.get(ptype) if callback: - await callback(packet) + try: + await callback(packet) + except asyncio.CancelledError as e: + # TODO: log error + print("HEHEHEHEY, CANCELLEDERROR", e) + pass + + def _check_for_errors(self, packet): + # TODO: log throttled + + if "error" in packet: + raise utils.ResponseError(response.get("error")) async def _handle_bounce(self, packet): - pass # TODO + """ + From api.euphoria.io: + A bounce-event indicates that access to a room is denied. + """ + + data = packet.get("data") + + await self.controller.on_bounce( + reason=data.get("reason", None), + auth_options=data.get("auth_options", None), + agent_id=data.get("agent_id", None), + ip=data.get("ip", None) + ) async def _handle_disconnect(self, packet): - pass # TODO + """ + From api.euphoria.io: + A disconnect-event indicates that the session is being closed. The + client will subsequently be disconnected. + + If the disconnect reason is “authentication changed”, the client should + immediately reconnect. + """ + + data = packet.get("data") + + await self.controller.on_disconnect(data.get("reason")) async def _handle_hello(self, packet): """ @@ -150,10 +211,11 @@ class Room: 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") + self.account = data.get("account", None) + self.account_has_access = data.get("account_has_access", None) + self.account_email_verified = data.get("account_email_verified", None) await self.controller.on_hello( data.get("id"), @@ -166,7 +228,18 @@ class Room: ) async def _handle_join(self, packet): - pass # TODO + """ + From api.euphoria.io: + A join-event indicates a session just joined the room. + """ + + data = packet.get("data") + session = utils.Session.from_dict(data) + + # update self.listing + self.listing.add(session) + + await self.controller.on_join(session) async def _handle_login(self, packet): pass # TODO @@ -178,13 +251,43 @@ class Room: pass # TODO async def _handle_nick(self, packet): - pass # TODO + """ + From api.euphoria.io: + nick-event announces a nick change by another session in the room. + """ + + data = packet.get("data") + session_id = data.get("session_id") + to_nick = data.get("to") + + # update self.listing + session = self.listing.by_sid(session_id) + if session: + session.nick = to_nick + + await self.controller.on_nick( + session_id, + data.get("id"), + data.get("from"), + to_nick + ) async def _handle_edit_message(self, packet): pass # TODO async def _handle_part(self, packet): - pass # TODO + """ + From api.euphoria.io: + A part-event indicates a session just disconnected from the room. + """ + + data = packet.get("data") + session = utils.Session.from_dict(data) + + # update self.listing + self.listing.remove(session.session_id) + + await self.controller.on_part(session) async def _handle_ping(self, packet): """ @@ -205,26 +308,37 @@ class Room: pass # TODO async def _handle_send(self, packet): - pass # TODO + """ + From api.euphoria.io: + A send-event indicates a message received by the room from another + session. + """ + + data = packet.get("data") + message = utils.Message.from_dict(data) + + await self.controller.on_send(message) async def _handle_snapshot(self, packet): """ + From api.euphoria.io: A snapshot-event indicates that a session has successfully joined a room. It also offers a snapshot of the room’s state and recent history. """ data = packet.get("data") - for session_data in data.get("listing"): - session = utils.Session.from_dict(session_data) + sessions = [utils.Session.from_dict(d) for d in data.get("listing")] + messages = [utils.Message.from_dict(d) for d in data.get("log")] + + # update self.listing + for session in sessions: self.listing.add(session) - - log = [utils.Message.from_dict(d) for d in data.get("log")] - self.session.nick = data.get("nick") + self.session.nick = data.get("nick", None) - self.pm_with_nick = data.get("pm_with_nick"), - self.pm_with_user_id = data.get("pm_with_user_id") + self.pm_with_nick = data.get("pm_with_nick", None), + self.pm_with_user_id = data.get("pm_with_user_id", None) await self.controller.on_connected() @@ -232,8 +346,8 @@ class Room: data.get("identity"), data.get("session_id"), self.version, - self.listing, - log, + sessions, # listing + messages, # log nick=self.session.nick, pm_with_nick=self.pm_with_nick, pm_with_user_id=self.pm_with_user_id diff --git a/yaboli/utils.py b/yaboli/utils.py index a9f0e6f..3401d4c 100644 --- a/yaboli/utils.py +++ b/yaboli/utils.py @@ -1,8 +1,23 @@ +import re + +__all__ = ["mention", "mention_reduced", "similar", "Session", "Listing", "Message", "Log"] + + + +def mention(nick): + return "".join(c for c in nick if c not in ".!?;&<'\"" and not c.isspace()) + +def mention_reduced(nick): + return mention(nick).lower() + +def similar(nick1, nick2): + return mention_reduced(nick1) == mention_reduced(nick2) + class Session: - def __init__(self, user_id, name, server_id, server_era, session_id, is_staff=None, + def __init__(self, user_id, nick, 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.nick = nick self.server_id = server_id self.server_era = server_era self.session_id = session_id @@ -19,10 +34,10 @@ class Session: 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") + d.get("is_staff", None), + d.get("is_manager", None), + d.get("client_address", None), + d.get("real_address", None) ) @property @@ -86,10 +101,13 @@ class Message(): d.get("time"), Session.from_dict(d.get("sender")), d.get("content"), - d.get("parent"), - d.get("previous_edit_id"), - d.get("encryption_key"), - d.get("edited"), - d.get("deleted"), - d.get("truncated") + d.get("parent", None), + d.get("previous_edit_id", None), + d.get("encryption_key", None), + d.get("edited", None), + d.get("deleted", None), + d.get("truncated", None) ) + +class Log: + pass # TODO