From: Andrew Gundersen Date: Sat, 21 Nov 2020 20:43:17 +0000 (-0600) Subject: working v1 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=5b54f3f6360de72c6ad5d4dee7c3b505e5ed12b9;p=abc-messenger working v1 --- 5b54f3f6360de72c6ad5d4dee7c3b505e5ed12b9 diff --git a/__pycache__/agent.cpython-38.pyc b/__pycache__/agent.cpython-38.pyc new file mode 100644 index 0000000..2fc5689 Binary files /dev/null and b/__pycache__/agent.cpython-38.pyc differ diff --git a/__pycache__/mail.cpython-38.pyc b/__pycache__/mail.cpython-38.pyc new file mode 100644 index 0000000..6a79f0a Binary files /dev/null and b/__pycache__/mail.cpython-38.pyc differ diff --git a/__pycache__/mailroom.cpython-38.pyc b/__pycache__/mailroom.cpython-38.pyc new file mode 100644 index 0000000..cba747d Binary files /dev/null and b/__pycache__/mailroom.cpython-38.pyc differ diff --git a/__pycache__/message.cpython-38.pyc b/__pycache__/message.cpython-38.pyc new file mode 100644 index 0000000..77079d0 Binary files /dev/null and b/__pycache__/message.cpython-38.pyc differ diff --git a/__pycache__/messenger.cpython-38.pyc b/__pycache__/messenger.cpython-38.pyc new file mode 100644 index 0000000..da20a91 Binary files /dev/null and b/__pycache__/messenger.cpython-38.pyc differ diff --git a/__pycache__/schemes.cpython-38.pyc b/__pycache__/schemes.cpython-38.pyc new file mode 100644 index 0000000..05076b9 Binary files /dev/null and b/__pycache__/schemes.cpython-38.pyc differ diff --git a/__pycache__/sdk.cpython-38.pyc b/__pycache__/sdk.cpython-38.pyc new file mode 100644 index 0000000..a4257df Binary files /dev/null and b/__pycache__/sdk.cpython-38.pyc differ diff --git a/__pycache__/server.cpython-38.pyc b/__pycache__/server.cpython-38.pyc new file mode 100644 index 0000000..ff30f0d Binary files /dev/null and b/__pycache__/server.cpython-38.pyc differ diff --git a/__pycache__/session.cpython-38.pyc b/__pycache__/session.cpython-38.pyc new file mode 100644 index 0000000..5c968ec Binary files /dev/null and b/__pycache__/session.cpython-38.pyc differ diff --git a/__pycache__/typs.cpython-38.pyc b/__pycache__/typs.cpython-38.pyc new file mode 100644 index 0000000..d33211d Binary files /dev/null and b/__pycache__/typs.cpython-38.pyc differ diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..3d9c283 --- /dev/null +++ b/agent.py @@ -0,0 +1,98 @@ +import json +import asyncio +import websockets + +import typs + + +class Agent: + """Session interface for Crimata Agent + + Receive core functionalities for communicating with Crimata + Agent in an OOP way. + + """ + def __init__(self, connection): + self.connection = connection + + # Submit a request for info, return response. + # Must disclose what service it's for. + async def fetch(self, entities, service): + + # Assert list. + if type(entities) == str: + entities = [entities] + + print(f"Fetching {[e for e in entities]} for {service}") + + # Build intent. + params = { + "service": service, + "entities": entities + }; intent = typs.Intent("fetch", params) + + # Send intent to agent, wait for response. + await self.send_agent_intent(intent) + response = await self.recv_agent_intent() + + # Parse response and return entities. + entities = response.params.get("found") + print(f"Fetch response: {entities}") + return entities + + # Text the client something. Also, let agent know + # when your service is complete via the complete + # attribute. + # Must disclose what service it's for. + async def notify(self, text, end=False): + + print(f"Notifying: {text} for {self.service}") + + params = { + "service": self.service, + "text": text, + "end": end + } + + # Build the intent + intent = typs.Intent("notify", params) + + # Send intent to agent. + await self.send_agent_intent(intent) + + # Get user info. + async def sync(self): + + intent = typs.Intent("sync", params={}) + + # Send intent to agent. + await self.send_agent_intent(intent) + + # Recv and return profile. + intent = await self.recv_agent_intent() + profile = intent.params.get("profile") + return profile + + async def recv_agent_intent(self): + package = await self.connection.recv() + intent = self.__decode(package) + return intent + + async def send_agent_intent(self, intent): + package = self.__encode(intent) + await self.connection.send(package) + + def __encode(self, intent: typs.Intent): + package = json.dumps(intent.__dict__) + return package + + def __decode(self, package) -> typs.Intent: + jpackage = json.loads(package) + name = jpackage.get("name") + params = jpackage.get("params") + intent = typs.Intent(name, params) + return intent + + + + diff --git a/mail.py b/mail.py new file mode 100644 index 0000000..489b2a0 --- /dev/null +++ b/mail.py @@ -0,0 +1,42 @@ +# mail.py + +import typs +import mailroom + + +class Mail: + """Session interface for mailroom. + + Two main IO methods. Will translate intents to mail and vice versa. + """ + def __init__(self): + pass + + # Receive intent. + async def mailbox_recv(self): + mail = await mailroom.get_mail(self.crimata_id) + intent = self.__decode(mail) + return intent + + # Send intent. + def mailbox_send(self, intent): + mail = self.__encode(intent) + mailroom.put_mail(self.crimata_id, mail) + + def __encode(self, intent: typs.Intent) -> typs.Mail: + p = intent.params + owner = self.crimata_id + target = p.get("target") + text = p.get("text") + mail = typs.Mail(owner, target, text) + return mail + + @staticmethod + def __decode(mail: typs.Mail) -> typs.Intent: + name = "notify" + params = { + "text": mail.text #dev only + } + intent = typs.Intent(name, params) + return intent + \ No newline at end of file diff --git a/mailroom.py b/mailroom.py new file mode 100644 index 0000000..7e0d0a2 --- /dev/null +++ b/mailroom.py @@ -0,0 +1,42 @@ +# mailroom.py + +import queue +import asyncio + +import typs + +# Subcribers (Crimata IDs) +subs = [] + +# Mailroom (all mailboxes) +que = queue.Queue() +ref = {} + +# Mailroom functions +# ------------------ + +# Create mailbox and add to mailroom. +def create_mailbox(crimata_id): + mailbox = typs.MailBox(name=crimata_id) + que.put(mailbox) + ref.update({crimata_id: mailbox}) + subs.append(crimata_id) + +# Get mailbox by Crimata ID. +def get_mailbox(crimata_id): + if not crimata_id in subs: + create_mailbox(crimata_id) + mailbox = ref.get(crimata_id) + return mailbox + +# Inbox -> receiving mail. +async def get_mail(crimata_id): + mailbox = get_mailbox(crimata_id) + mail = await mailbox.inbox.get() + return mail + +# Outbox -> sending mail. +def put_mail(crimata_id, mail: typs.Mail): + mailbox = get_mailbox(crimata_id) + mailbox.outbox.put(mail) + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..c6ac2fa --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +# server.py + +import typs +import queue +import asyncio +import websockets + +import server +import message + + +# Run the ws server and the messenger concurrently. +async def main(): + print(f"Crimata Messenger") + await asyncio.gather(server.lift(), message.run()) + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/message.py b/message.py new file mode 100644 index 0000000..76a1c02 --- /dev/null +++ b/message.py @@ -0,0 +1,37 @@ +# messenge.py + +import asyncio + +import mailroom + + +# Independent loop that updates mailboxes. +async def messenger(): + + print("Running Messenger") + + while True: + + # Yield if no mailboxes. + if mailroom.que.empty(): + await asyncio.sleep(1) + continue + + # Grab a mailbox. + mailbox = mailroom.que.get() + print(f"Handling mailbox: {mailbox.name}.") + + # Handle every message in outbox. + while not mailbox.outbox.empty(): + message = mailbox.outbox.get() + + # Get mailbox of target and put mail there. + target_mailbox = mailroom.get_mailbox(message.target) + await target_mailbox.inbox.put(message) + + # Put the mailbox back + mailroom.que.put(mailbox) + await asyncio.sleep(1) + +async def run(): + await messenger() \ No newline at end of file diff --git a/schemes.py b/schemes.py new file mode 100644 index 0000000..1703a86 --- /dev/null +++ b/schemes.py @@ -0,0 +1,30 @@ +# schemes.py + + +class Schemes: + + def __init__(self): + self.service = None + + async def handle_intent(self, intent): + print(f"Handling intent {intent.name}.") + + # Set the service + self.service = intent.name + + if intent.name == "init": + await self.init(intent) + if intent.name == "message": + self.message(intent) + + async def init(self, intent): + self.crimata_id = intent.params.get("crimata_id") + await self.notify("Messaging is live.") + + def message(self, intent): + text = intent.params.get("text") + target = intent.params.get("target") #crimata_id + print(f"Messaging {target}: '{text}'") + + # Uses mail.Mail interface. + self.mailbox_send(intent) \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..58ce7b8 --- /dev/null +++ b/server.py @@ -0,0 +1,27 @@ +# server.py + +import asyncio +import websockets + +import session + +HOST = "localhost" +PORT = 8764 + + +# Makes instance of a Messenger session for every client that connects. +async def launch_session(connection, path): + s = session.Session(connection) + + await asyncio.gather( + + s.do_agent_intents(), + + s.deliver_mail() + + ) + +# Run run for every new connection. +async def lift(): + print(f"Listening for connections on {HOST}:{PORT}") + await websockets.serve(launch_session, HOST, PORT) \ No newline at end of file diff --git a/session.py b/session.py new file mode 100644 index 0000000..29a1d37 --- /dev/null +++ b/session.py @@ -0,0 +1,81 @@ +# session.py + +import time +import asyncio + +import mail +import agent +import schemes + + +class Session(schemes.Schemes, agent.Agent, mail.Mail): + """Launch instance for every client connection. + + Will recv messages from client and push them to mailbox. + Also, will pull messages from mailbox and send to client. + + """ + def __init__(self, connection): + agent.Agent.__init__(self, connection) + + self.crimata_id = False + + print("Launched new session") + + # Recv intents run desired endpoint. + async def do_agent_intents(self): + while True: + + # Recv intent from agent. + intent = await self.recv_agent_intent() + print(f"Intent: {intent.name}") + + # Call corresponding endpoint. + await self.handle_intent(intent) + + await asyncio.sleep(0.1) + + # Send mail back to agent. + async def deliver_mail(self): + while True: + + # Sleep until Crimata ID. + if not self.crimata_id: + await asyncio.sleep(1) + continue + + intent = await self.mailbox_recv() + await self.send_agent_intent(intent) + + await asyncio.sleep(0.1) + + + + + + + + + + + + + + + + + + # async def hello(self): + + # intent = await self.recv_agent_intent() + + # response = await self.fetch("crimata_id", service="message") + # self.profile = response.get("profile") + + # target = intent.params.get("target") + # await self.notify(f"Messenger APP: Received Message Intent", + # service="message", complete=True) + + # print("Shutting down in 10s") + + # await asyncio.sleep(10) diff --git a/typs.py b/typs.py new file mode 100644 index 0000000..2ef992f --- /dev/null +++ b/typs.py @@ -0,0 +1,28 @@ +# typs.py + +import queue +import asyncio + + +class Mail: + + def __init__(self, owner, target, text): + self.owner = owner + self.target = target + self.text = text + + +class MailBox: + + def __init__(self, name): + + self.name = name # just for logging purposes. + self.inbox = asyncio.Queue() + self.outbox = queue.Queue() + + +class Intent: + + def __init__(self, name, params: dict): + self.name = name + self.params = params \ No newline at end of file