commit 40b8bd42fd3f2f2ea38fce342a421ebf09812829 Author: retoor Date: Thu Dec 5 19:34:58 2024 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7d91b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode +rchat.db* +config.py +.venv +.history +__pycache__ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..35cc240 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +BIN = ./.venv/bin/ +PYTHON = ./.venv/bin/python +PIP = ./.venv/bin/pip + +APP_NAME=rchat + +all: ensure_repo ensure_env format install build + +ensure_repo: + -@git init + +ensure_env: + -@python3 -m venv .venv + +install: + $(PIP) install -e . + +format: + $(PIP) install shed + . $(BIN)/activate && shed + +build: format install + $(PIP) install build + $(PYTHON) -m build + +run: + $(BIN)rchat.serve + diff --git a/dist/ragnar-1.3.37.tar.gz b/dist/ragnar-1.3.37.tar.gz new file mode 100644 index 0000000..35eb215 Binary files /dev/null and b/dist/ragnar-1.3.37.tar.gz differ diff --git a/dist/rchat-1.0.0-py3-none-any.whl b/dist/rchat-1.0.0-py3-none-any.whl new file mode 100644 index 0000000..6355929 Binary files /dev/null and b/dist/rchat-1.0.0-py3-none-any.whl differ diff --git a/dist/rchat-1.0.0.tar.gz b/dist/rchat-1.0.0.tar.gz new file mode 100644 index 0000000..9d5584d Binary files /dev/null and b/dist/rchat-1.0.0.tar.gz differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..07de284 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..8796ddd --- /dev/null +++ b/setup.cfg @@ -0,0 +1,28 @@ +[metadata] +name = rchat +version = 1.0.0 +description = rchat +author = retoor +author_email = retoor@molodetz.nl +license = MIT +long_description = file: README.md +long_description_content_type = text/markdown + +[options] +packages = find: +package_dir = + = src +python_requires = >=3.7 +install_requires = + aiohttp + faker + dataset + app @ git+https://retoor.molodetz.nl/retoor/app.git + mololog @ git+https://retoor.molodetz.nl/retoor/mololog.git + +[options.packages.find] +where = src + +[options.entry_points] +console_scripts = + rchat.serve = rchat.__main__:main diff --git a/src/rchat.egg-info/PKG-INFO b/src/rchat.egg-info/PKG-INFO new file mode 100644 index 0000000..5ed40a3 --- /dev/null +++ b/src/rchat.egg-info/PKG-INFO @@ -0,0 +1,14 @@ +Metadata-Version: 2.1 +Name: rchat +Version: 1.0.0 +Summary: rchat +Author: retoor +Author-email: retoor@molodetz.nl +License: MIT +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Requires-Dist: aiohttp +Requires-Dist: faker +Requires-Dist: dataset +Requires-Dist: app@ git+https://retoor.molodetz.nl/retoor/app.git +Requires-Dist: mololog@ git+https://retoor.molodetz.nl/retoor/mololog.git diff --git a/src/rchat.egg-info/SOURCES.txt b/src/rchat.egg-info/SOURCES.txt new file mode 100644 index 0000000..cd2cf3b --- /dev/null +++ b/src/rchat.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +pyproject.toml +setup.cfg +src/rchat/__init__.py +src/rchat/__main__.py +src/rchat/app.py +src/rchat/faker.py +src/rchat.egg-info/PKG-INFO +src/rchat.egg-info/SOURCES.txt +src/rchat.egg-info/dependency_links.txt +src/rchat.egg-info/entry_points.txt +src/rchat.egg-info/requires.txt +src/rchat.egg-info/top_level.txt \ No newline at end of file diff --git a/src/rchat.egg-info/dependency_links.txt b/src/rchat.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/rchat.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/rchat.egg-info/entry_points.txt b/src/rchat.egg-info/entry_points.txt new file mode 100644 index 0000000..766c935 --- /dev/null +++ b/src/rchat.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +rchat.serve = rchat.__main__:main diff --git a/src/rchat.egg-info/requires.txt b/src/rchat.egg-info/requires.txt new file mode 100644 index 0000000..2bd7e86 --- /dev/null +++ b/src/rchat.egg-info/requires.txt @@ -0,0 +1,5 @@ +aiohttp +faker +dataset +app@ git+https://retoor.molodetz.nl/retoor/app.git +mololog@ git+https://retoor.molodetz.nl/retoor/mololog.git diff --git a/src/rchat.egg-info/top_level.txt b/src/rchat.egg-info/top_level.txt new file mode 100644 index 0000000..e189468 --- /dev/null +++ b/src/rchat.egg-info/top_level.txt @@ -0,0 +1 @@ +rchat diff --git a/src/rchat/__init__.py b/src/rchat/__init__.py new file mode 100644 index 0000000..a2d757f --- /dev/null +++ b/src/rchat/__init__.py @@ -0,0 +1,5 @@ +import logging + +logging.basicConfig(level=logging.INFO) + +log = logging.getLogger(__name__) diff --git a/src/rchat/__main__.py b/src/rchat/__main__.py new file mode 100644 index 0000000..a5ed602 --- /dev/null +++ b/src/rchat/__main__.py @@ -0,0 +1,14 @@ +from aiohttp import web + +from rchat.app import create_app + + +def main(): + app = create_app() + web.run_app(app, port=8080) + + +# Run the server +if __name__ == "__main__": + main() +# static/index.html diff --git a/src/rchat/app.py b/src/rchat/app.py new file mode 100644 index 0000000..551adf9 --- /dev/null +++ b/src/rchat/app.py @@ -0,0 +1,134 @@ +# server.py +import json +import pathlib +import uuid + +import aiohttp +from aiohttp import web +from app.app import Application as BaseApplication +from mololog.client import patch + +from rchat.faker import fake + +patch("https://mololog.molodetz.nl/") + + +class Application(BaseApplication): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.clients = {} + self.sessions = {} + self.base_folder = pathlib.Path(__file__).parent + self.static_folder = self.base_folder.joinpath("static") + self.router.add_get("/ws", self.websocket_handler) + self.router.add_get("/", self.index_handler) + self.router.add_static("/", path=self.static_folder, name="static") + + async def index_handler(self, request): + content = self.static_folder.joinpath("index.html") + + return web.Response(body=content.read_text(), content_type="text/html") + + async def websocket_handler(self, request): + ws = web.WebSocketResponse() + await ws.prepare(request) + session = request.session + if not 'username' in session: + session['username'] = fake.name().split(" ")[0] + session['uid'] = str(uuid.uuid4()) + session['session_id'] = session['uid'] + + username = session["username"] + session_id = session["session_id"] + uid = session["uid"] + + response = ws + response.set_cookie( + samesite=True, + name="rchat_session", + value=session_id, + max_age=365 * 24 * 60 * 60, + httponly=True, + ) + + try: + + for message in await self.find("messages", {}): + await ws.send_json(message) + + await ws.send_json( + { + "type": "system", + "message": f'Welcome, your name is {session["username"]}.', + } + ) + + await self.broadcast( + {"type": "system", "message": f"{username} joined the chat."} + ) + + self.clients[uid] = ws + + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + try: + data = json.loads(msg.data) + + message = data.get("message") + if message: + if message.startswith("/nick "): + original_name = session["username"] + session["username"] = message[len("/nick ") :] + + await self.broadcast( + { + "type": "system", + "message": f'{original_name} is renamed to {session["username"]}.', + } + ) + + continue + + await self.broadcast( + { + "type": "chat", + "sender": session["username"], + "message": data.get("message", ""), + } + ) + + except json.JSONDecodeError: + await ws.send_json( + {"type": "error", "message": "Invalid message format."} + ) + + elif msg.type == aiohttp.WSMsgType.ERROR: + print( + f"WebSocket connection closed with exception {ws.exception()}." + ) + + finally: + if uid in self.clients: + del self.clients[uid] + await self.broadcast( + {"type": "system", "message": f"{username} left the chat."}, + exclude=uid, + ) + + return ws + + async def broadcast(self, message, exclude=None): + await self.insert("messages", message) + for uid, client_ws in self.clients.items(): + if uid == exclude: + continue + try: + await client_ws.send_json(message) + except Exception as e: + print(f"Error sending to client {uid}: {e}.") + + +def create_app(): + app = Application() + + return app diff --git a/src/rchat/faker.py b/src/rchat/faker.py new file mode 100644 index 0000000..3b09bd3 --- /dev/null +++ b/src/rchat/faker.py @@ -0,0 +1,3 @@ +from faker import Faker + +fake = Faker() diff --git a/src/rchat/static/index.html b/src/rchat/static/index.html new file mode 100644 index 0000000..936dcec --- /dev/null +++ b/src/rchat/static/index.html @@ -0,0 +1,204 @@ + + + + + rchat + + + + +
+
+
+ + + +
+
+ + + + + \ No newline at end of file