main
py 81 lines 2.02 KB
Raw
1 import importlib
2 import inspect
3 import json
4 from typing import Any, TypedDict
5 import aiohttp
6 from helpers import crypto
7
8 from helpers import dotenv
9
10
11 # Remote Function Call library
12 # Call function via http request
13 # Secured by pre-shared key
14
15
16 class RFCInput(TypedDict):
17 module: str
18 function_name: str
19 args: list[Any]
20 kwargs: dict[str, Any]
21
22
23 class RFCCall(TypedDict):
24 rfc_input: str
25 hash: str
26
27
28 async def call_rfc(
29 url: str, password: str, module: str, function_name: str, args: list, kwargs: dict
30 ):
31 input = RFCInput(
32 module=module,
33 function_name=function_name,
34 args=args,
35 kwargs=kwargs,
36 )
37 call = RFCCall(
38 rfc_input=json.dumps(input), hash=crypto.hash_data(json.dumps(input), password)
39 )
40 result = await _send_json_data(url, call)
41 return result
42
43
44 async def handle_rfc(rfc_call: RFCCall, password: str):
45 if not crypto.verify_data(rfc_call["rfc_input"], rfc_call["hash"], password):
46 raise Exception("Invalid RFC hash")
47
48 input: RFCInput = json.loads(rfc_call["rfc_input"])
49 return await _call_function(
50 input["module"], input["function_name"], *input["args"], **input["kwargs"]
51 )
52
53
54 async def _call_function(module: str, function_name: str, *args, **kwargs):
55 func = _get_function(module, function_name)
56 if inspect.iscoroutinefunction(func):
57 return await func(*args, **kwargs)
58 else:
59 return func(*args, **kwargs)
60
61
62 def _get_function(module: str, function_name: str):
63 # import module
64 imp = importlib.import_module(module)
65 # get function by the name
66 func = getattr(imp, function_name)
67 return func
68
69
70 async def _send_json_data(url: str, data):
71 async with aiohttp.ClientSession() as session:
72 async with session.post(
73 url,
74 json=data,
75 ) as response:
76 if response.status == 200:
77 result = await response.json()
78 return result
79 else:
80 error = await response.text()
81 raise Exception(error)