Compare commits
2
Commits
5cd636d340
...
e0a9408b01
| 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 asyncio
|
||||||
import json, random, re, math, time, traceback
|
import json, random, re, math, time, traceback
|
||||||
|
from websockets.exceptions import ConnectionClosed, ConnectionClosedOK
|
||||||
from websockets.asyncio.server import serve
|
from websockets.asyncio.server import serve
|
||||||
|
|
||||||
from db import Database
|
from db import Database
|
||||||
@@ -48,6 +49,12 @@ def serverError(msg,**kwargs) -> str:
|
|||||||
sessionUsers = {}
|
sessionUsers = {}
|
||||||
sessionCreatedAccount = []
|
sessionCreatedAccount = []
|
||||||
|
|
||||||
|
def forgetSession(sid):
|
||||||
|
if sid in sessionUsers:
|
||||||
|
del sessionUsers[sid]
|
||||||
|
if sid in sessionCreatedAccount:
|
||||||
|
sessionCreatedAccount.remove(sid)
|
||||||
|
|
||||||
async def handleCommand(ws,cmd):
|
async def handleCommand(ws,cmd):
|
||||||
global sessionUsers
|
global sessionUsers
|
||||||
print(f"recieved command: {cmd} (from {id(ws)})")
|
print(f"recieved command: {cmd} (from {id(ws)})")
|
||||||
@@ -59,6 +66,8 @@ async def handleCommand(ws,cmd):
|
|||||||
match cmd["type"]:
|
match cmd["type"]:
|
||||||
case "ping":
|
case "ping":
|
||||||
return await ws.send(response("Pong!"))
|
return await ws.send(response("Pong!"))
|
||||||
|
|
||||||
|
# accounts
|
||||||
case "createuser":
|
case "createuser":
|
||||||
if uid != None:
|
if uid != None:
|
||||||
return await ws.send(clientError("You are already logged in."))
|
return await ws.send(clientError("You are already logged in."))
|
||||||
@@ -163,9 +172,19 @@ async def serveClient(ws):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exception(e)
|
traceback.print_exception(e)
|
||||||
await ws.send(serverError(f"Cannot process command ({cmd["type"]})"))
|
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:
|
except Exception as e:
|
||||||
traceback.print_exception(e)
|
traceback.print_exception(e)
|
||||||
await ws.send(serverError("Cannot process JSON."))
|
await ws.send(serverError("Cannot parse command."))
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
async with serve(serveClient, "", 30341) as server:
|
async with serve(serveClient, "", 30341) as server:
|
||||||
|
|||||||
Reference in New Issue
Block a user