Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0a9408b01 | ||
|
|
384ba26170 |
@@ -0,0 +1,157 @@
|
||||
import asyncio, queue, json, threading, hashlib, websockets, inspect
|
||||
from websockets.asyncio.client import connect as wsconnect
|
||||
|
||||
class BasicServerConnection():
|
||||
def __init__(self,uri: str) -> None:
|
||||
self.uri = uri
|
||||
self.callbacks = {}
|
||||
self.resq = queue.Queue()
|
||||
self.asklock = asyncio.Lock()
|
||||
self.needsClose = threading.Event()
|
||||
|
||||
def addCallback(self,name,func):
|
||||
# print("addcallback",name,func)
|
||||
if name in self.callbacks:
|
||||
self.callbacks[name].append(func)
|
||||
else:
|
||||
self.callbacks[name] = [func]
|
||||
|
||||
def removeCallback(self,name,func):
|
||||
if not name in self.callbacks: return
|
||||
self.callbacks[name].remove(func)
|
||||
|
||||
async def __runCallback(self,name,*args):
|
||||
# print("runcallback",name,args)
|
||||
if not name in self.callbacks: return
|
||||
for func in self.callbacks[name]:
|
||||
# print(func)
|
||||
if inspect.iscoroutinefunction(func):
|
||||
await func(*args)
|
||||
else:
|
||||
func(*args)
|
||||
|
||||
def __runCallbackInThread(self,name,*args):
|
||||
def __run():
|
||||
asyncio.run(self.__runCallback(name,*args))
|
||||
thread = threading.Thread(target=__run)
|
||||
thread.start()
|
||||
|
||||
async def connect(self) -> None:
|
||||
uri = self.uri
|
||||
async with wsconnect(uri) as ws:
|
||||
self.ws = ws
|
||||
while True:
|
||||
if self.needsClose.is_set():
|
||||
await self.ws.close()
|
||||
break
|
||||
try:
|
||||
txt = await asyncio.wait_for(ws.recv(), timeout=0.1)
|
||||
# print("recv",txt)
|
||||
res = json.loads(txt)
|
||||
if "connected" == res["type"]:
|
||||
self.__runCallbackInThread("connect")
|
||||
if res["type"] in ["response","error"]:
|
||||
self.resq.put(res)
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
break
|
||||
except TimeoutError:
|
||||
pass
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
if not self.ws: return
|
||||
self.needsClose.set()
|
||||
await self.__runCallback("connect")
|
||||
|
||||
async def waitConnect(self) -> None:
|
||||
# wait for it to reconnect
|
||||
# print("waiting for reconnection.")
|
||||
event = threading.Event()
|
||||
def evset():
|
||||
event.set()
|
||||
self.addCallback("connect",evset)
|
||||
self.addCallback("disconnect",evset)
|
||||
#self.callbacks["connect"].insert(0,evset)
|
||||
await asyncio.to_thread(event.wait)
|
||||
# print("reconnected!! hell yeah")
|
||||
self.removeCallback("connect",evset)
|
||||
self.removeCallback("disconnect",evset)
|
||||
|
||||
async def ask(self,obj: dict,t:int=None) -> dict | None:
|
||||
# print("ask",obj)
|
||||
while True:
|
||||
if self.needsClose.is_set():
|
||||
return None
|
||||
try:
|
||||
async with self.asklock:
|
||||
await self.ws.send(json.dumps(obj))
|
||||
e = self.resq.get(timeout=t)
|
||||
except queue.Empty:
|
||||
e = None
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
await self.waitConnect()
|
||||
continue
|
||||
return e
|
||||
|
||||
class ServerConnection(BasicServerConnection):
|
||||
def __init__(self,uri: str = "ws://localhost:30341"):
|
||||
super().__init__(uri)
|
||||
self.logincmd = None
|
||||
super().addCallback("connect",self.__login)
|
||||
|
||||
async def __connectloop(self):
|
||||
self.mustConnect = True
|
||||
first = True
|
||||
while self.mustConnect:
|
||||
if not first:
|
||||
await asyncio.sleep(1)
|
||||
first=False
|
||||
try:
|
||||
await super().connect()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
async def connect(self):
|
||||
self.thread = threading.Thread(target=lambda: asyncio.run(self.__connectloop()))
|
||||
self.thread.start()
|
||||
await super().waitConnect()
|
||||
|
||||
async def disconnect(self):
|
||||
self.mustConnect = False
|
||||
await super().disconnect()
|
||||
self.thread.join()
|
||||
|
||||
async def status(self):
|
||||
return await super().ask({"type": "status"})
|
||||
|
||||
async def __login(self):
|
||||
# print("__login")
|
||||
if self.logincmd == None: return {"type": "error", "error": "No credentials found.", "server": False}
|
||||
stat = await self.status()
|
||||
if stat==None: return
|
||||
if stat["type"]=="error" or stat["response"]["login"]==False:
|
||||
return await super().ask(self.logincmd)
|
||||
|
||||
async def login(self,name: str,pwrd: str):
|
||||
res = hashlib.md5(pwrd.encode("utf8"))
|
||||
psum = res.hexdigest()
|
||||
self.logincmd = {"type": "login", "name": name, "pass": psum}
|
||||
return await self.__login()
|
||||
|
||||
async def logout(self):
|
||||
self.logincmd = None
|
||||
return await super().ask({"type": "logout"})
|
||||
|
||||
async def createuser(self,name: str,pwrd: str):
|
||||
res = hashlib.md5(pwrd.encode("utf8"))
|
||||
psum = res.hexdigest()
|
||||
return await super().ask({"type": "createuser", "name": name, "pass": psum})
|
||||
|
||||
async def changename(self,name: str):
|
||||
return await super().ask({"type": "changename", "name": name})
|
||||
|
||||
async def changepass(self,old: str,new: str):
|
||||
osum = hashlib.md5(old.encode("utf8")).hexdigest()
|
||||
nsum = hashlib.md5(new.encode("utf8")).hexdigest()
|
||||
return await super().ask({"type": "changepass", "oldpass": osum, "newpass": nsum})
|
||||
+20
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
import json, random, re, math, time, traceback
|
||||
from websockets.exceptions import ConnectionClosed, ConnectionClosedOK
|
||||
from websockets.asyncio.server import serve
|
||||
|
||||
from db import Database
|
||||
@@ -48,6 +49,12 @@ def serverError(msg,**kwargs) -> str:
|
||||
sessionUsers = {}
|
||||
sessionCreatedAccount = []
|
||||
|
||||
def forgetSession(sid):
|
||||
if sid in sessionUsers:
|
||||
del sessionUsers[sid]
|
||||
if sid in sessionCreatedAccount:
|
||||
sessionCreatedAccount.remove(sid)
|
||||
|
||||
async def handleCommand(ws,cmd):
|
||||
global sessionUsers
|
||||
print(f"recieved command: {cmd} (from {id(ws)})")
|
||||
@@ -59,6 +66,8 @@ async def handleCommand(ws,cmd):
|
||||
match cmd["type"]:
|
||||
case "ping":
|
||||
return await ws.send(response("Pong!"))
|
||||
|
||||
# accounts
|
||||
case "createuser":
|
||||
if uid != None:
|
||||
return await ws.send(clientError("You are already logged in."))
|
||||
@@ -163,9 +172,19 @@ async def serveClient(ws):
|
||||
except Exception as e:
|
||||
traceback.print_exception(e)
|
||||
await ws.send(serverError(f"Cannot process command ({cmd["type"]})"))
|
||||
except json.decoder.JSONDecodeError:
|
||||
await ws.send(serverError("Cannot process JSON."))
|
||||
except ConnectionClosedOK:
|
||||
print(f"connection {id(ws)} has closed")
|
||||
forgetSession(id(ws))
|
||||
break
|
||||
except ConnectionClosed as e:
|
||||
print(f"connection {id(ws)} has closed with a non-OK exit code:")
|
||||
traceback.print_exception(e)
|
||||
break
|
||||
except Exception as e:
|
||||
traceback.print_exception(e)
|
||||
await ws.send(serverError("Cannot process JSON."))
|
||||
await ws.send(serverError("Cannot parse command."))
|
||||
|
||||
async def main():
|
||||
async with serve(serveClient, "", 30341) as server:
|
||||
|
||||
Reference in New Issue
Block a user