From 792edb4c889654ffb743d7a2234ddace6ebd85bc Mon Sep 17 00:00:00 2001 From: Henry Le Grys Date: Fri, 23 Oct 2020 12:00:36 +0100 Subject: [PATCH] Add animesoul client & rpc server --- README.md | 33 ++++++++++++++++++++++ jaken/__init__.py | 22 +++++++++++++++ jaken/__main__.py | 5 ++++ jaken/rpc.py | 30 ++++++++++++++++++++ jaken/soul.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ 6 files changed, 163 insertions(+) create mode 100644 README.md create mode 100644 jaken/__init__.py create mode 100644 jaken/__main__.py create mode 100644 jaken/rpc.py create mode 100644 jaken/soul.py create mode 100644 requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b574e7 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# jaken + +Python component of Kirara, responds to RPC + +## Setup + +```sh +python3 -m venv env +./env/bin/pip install -r requirements.txt + +# running the server +./env/bin/python -m jaken +``` + +## RPC directory + +### `soul` + +Searching for cards: +```js +rpc.remote.soul.search_cards("blake").then((cards) => { + // `cards` is an array of cards +}); +``` + +Fetching a specific card by ID: +```js +rpc.remote.soul.get_card("5f2b3701a5a84e32a258df1b", /* with_users: */ true) + .then((data) => { + // `data.card` is a Card + // if with_users is true, `data.users` is an array of users + }); +``` diff --git a/jaken/__init__.py b/jaken/__init__.py new file mode 100644 index 0000000..2715c4c --- /dev/null +++ b/jaken/__init__.py @@ -0,0 +1,22 @@ +from jaken.rpc import SoulServer + +import aiomas +import asyncio + +async def main(): + soul_service = SoulServer() + await soul_service.connect() + + root_service = aiomas.rpc.ServiceDict({ + "soul": soul_service, + }) + + rpc_server = await aiomas.rpc.start_server(('localhost', 5444), root_service) + + try: + await soul_service.soul_client.run() + finally: + rpc_server.close() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/jaken/__main__.py b/jaken/__main__.py new file mode 100644 index 0000000..2ded24c --- /dev/null +++ b/jaken/__main__.py @@ -0,0 +1,5 @@ +import asyncio + +from jaken import main + +asyncio.run(main()) diff --git a/jaken/rpc.py b/jaken/rpc.py new file mode 100644 index 0000000..68dbea2 --- /dev/null +++ b/jaken/rpc.py @@ -0,0 +1,30 @@ +import aiomas + +from jaken.soul import SoulClient, Card + +class SoulServer: + router = aiomas.rpc.Service() + + def __init__(self): + self.soul_client = SoulClient() + + async def connect(self): + await self.soul_client.connect() + + @router.expose + async def search_cards(self, search_term): + r = await self.soul_client.call("cardindex", {"search": search_term}) + cards = [Card(doc).to_dict() for doc in r['data']['docs']] + + return cards + + @router.expose + async def get_card(self, card_id, with_users=False): + r = await self.soul_client.call("cardview", {"cardid": card_id}) + card = Card(r['card']).to_dict() + users = r['users'] + + if with_users: + return dict(card=card, users=users) + else: + return dict(card=card) diff --git a/jaken/soul.py b/jaken/soul.py new file mode 100644 index 0000000..9d235a9 --- /dev/null +++ b/jaken/soul.py @@ -0,0 +1,71 @@ +import asyncio +import socketio + +class PatchedAsyncClient(socketio.AsyncClient): + async def _handle_event(self, namespace, id, data): + namespace = namespace or '/' + await super()._handle_event(namespace, id, data) + await self._trigger_event('message', namespace, *data[1:]) + +class SoulClient: + def __init__(self): + self.sio = PatchedAsyncClient(logger=True) + self.sio.on('connect', self._on_connect) + self.sio.on('message', self._on_message) + + self.call_lock = asyncio.Lock() + self.callback = None + + async def _on_connect(self, namespace=None): + await self.call('init') + + async def _on_message(self, msg): + if self.call_lock.locked(): + self.call_lock.release() + if self.callback: + self.callback.set_result(msg) + + async def connect(self): + await self.sio.connect("wss://animesoul.com/socket.io/", transports=['websocket']) + + async def run(self): + await self.sio.wait() + + async def cast(self, event_name, data=None): + await self.sio.emit(event_name, data) + + async def call(self, event_name, data=None): + await self.call_lock.acquire() + await self.sio.emit(event_name, data) + + self.callback = asyncio.get_event_loop().create_future() + return await self.callback + +class Card: + IMAGE_CDN = "https://cdn.animesoul.com/images/cards" + + def __init__(self, kwargs): + self.pk = kwargs.get("_id") + self.name = kwargs.get("name") + self.slug = kwargs.get("slug") + self.tier = kwargs.get("tier") + self.claim_count = kwargs.get("claim_count") + + extra = kwargs.get("category") + try: + self.anime = extra[0] + except IndexError: + self.anime = None + + self.filename = kwargs.get("file") + self.url = f"{self.IMAGE_CDN}/{self.tier}/{self.filename}" + + def to_dict(self): + return { + "id": self.pk, + "name": self.name, + "tier": self.tier, + "anime": self.anime, + "link": self.url, + "claim_count": self.claim_count, + } diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..46e16ee --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +python-socketio[asyncio_client]==4.6.0 +aiomas==2.0.1