]> Repos - abc-messenger/commitdiff
working v1
authorAndrew Gundersen <gundersena@xavier.edu>
Sat, 21 Nov 2020 20:43:17 +0000 (14:43 -0600)
committerAndrew Gundersen <gundersena@xavier.edu>
Sat, 21 Nov 2020 20:43:17 +0000 (14:43 -0600)
19 files changed:
__pycache__/agent.cpython-38.pyc [new file with mode: 0644]
__pycache__/mail.cpython-38.pyc [new file with mode: 0644]
__pycache__/mailroom.cpython-38.pyc [new file with mode: 0644]
__pycache__/message.cpython-38.pyc [new file with mode: 0644]
__pycache__/messenger.cpython-38.pyc [new file with mode: 0644]
__pycache__/schemes.cpython-38.pyc [new file with mode: 0644]
__pycache__/sdk.cpython-38.pyc [new file with mode: 0644]
__pycache__/server.cpython-38.pyc [new file with mode: 0644]
__pycache__/session.cpython-38.pyc [new file with mode: 0644]
__pycache__/typs.cpython-38.pyc [new file with mode: 0644]
agent.py [new file with mode: 0644]
mail.py [new file with mode: 0644]
mailroom.py [new file with mode: 0644]
main.py [new file with mode: 0644]
message.py [new file with mode: 0644]
schemes.py [new file with mode: 0644]
server.py [new file with mode: 0644]
session.py [new file with mode: 0644]
typs.py [new file with mode: 0644]

diff --git a/__pycache__/agent.cpython-38.pyc b/__pycache__/agent.cpython-38.pyc
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..7e0d0a2
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..76a1c02
--- /dev/null
@@ -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 (file)
index 0000000..1703a86
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..29a1d37
--- /dev/null
@@ -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 (file)
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