commit 20f538367bc5b989d655104b70e7aa0d03c269cf Author: Auric Vente Date: Tue Feb 20 00:14:06 2024 -0600 First diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af8c156 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv/* +*.pyc +__pycache__/ +.mypy_cache/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..06bc75e --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +This is licensed under the DPL. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1b7bfb --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ + + +--- + +## Installation + +```shell +python -m venv venv +``` + +```shell +venv/bin/pip install -r requirements.txt +``` + +--- + +## Running + +Credentials are read from the environment. + +They're not stored in files. + +```shell +env GLUEBOT_USERNAME="yourUsername" GLUEBOT_PASSWORD="yourPassword" venv/bin/python main.py +``` + +--- + +## Configuration + +Modify `main.py` itself to edit what you need. + +Set the path to `gifmaker` and maybe change the `prefix`. + +You could make an alias for gifmaker: + +```shell +alias gifmaker="/path/to/venv/bin/python /path/to/gifmaker/src/main.py" +``` + +--- + +## Files + +Files generated through commands are stored in `/tmp/gifmaker` and removed when uploaded. + +--- + +## Commands + +> ,ping + +> ,describe Nick + +> ,wins Nick + +> ,numbers + +> ,date \ No newline at end of file diff --git a/describe.jpg b/describe.jpg new file mode 100644 index 0000000..0faf145 Binary files /dev/null and b/describe.jpg differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..c344752 --- /dev/null +++ b/main.py @@ -0,0 +1,210 @@ +import requests, websockets, asyncio, json, re, traceback, subprocess, os +from datetime import datetime +from pathlib import Path + +username = os.environ.get("GLUEBOT_USERNAME") +password = os.environ.get("GLUEBOT_PASSWORD") + +if not username or not password: + print("Missing environment variables") + exit(1) + +def get_time(): + return datetime.now().timestamp() + +def get_extension(path): + return Path(path).suffix.lower().lstrip(".") + +headers = { + "User-Agent": "renabot", + "Origin": "https://deek.chat", + "DNT": "1", +} + +cmd_date = get_time() +url = "https://deek.chat" +ws_url = "wss://deek.chat/ws" +prefix = "," +token = None +session = None +delay = 5 + +gifmaker = "gifmaker" +gm_common = "--font triplex --width 555 --nogrow --output /tmp/gifmaker" + +def update_time(): + global cmd_date + cmd_date = get_time() + +def blocked(): + return (get_time() - cmd_date) < delay + +def auth(): + global token, session, headers + data = {"name": username, "password": password, "submit": "log+in"} + res = requests.post(url + "/login/submit", headers=headers, data=data, allow_redirects=False) + token = re.search("(?:api_token)=[^;]+", res.headers.get("Set-Cookie")).group(0) + session = re.search("(?:session_id)=[^;]+", res.headers.get("Set-Cookie")).group(0) + headers["Cookie"] = token + "; " + session + +async def run(): + async with websockets.connect(ws_url, extra_headers=headers) as ws: + try: + while True: + message = await ws.recv() + await on_message(ws, message) + except KeyboardInterrupt: + return + except websockets.exceptions.ConnectionClosedOK: + print("WebSocket connection closed") + except Exception as e: + print("(WebSocket) Error:", e) + traceback.print_exc() + +async def on_message(ws, message): + if blocked(): return + + try: + data = json.loads(message) + except: + return + + if data["type"] == "message": + if data["data"]["name"] == username: + return + + text = data["data"]["text"].strip() + + if not text.startswith(prefix): + return + + room_id = data["roomId"] + words = text.lstrip(prefix).split(" ") + cmd = words[0] + args = words[1:] + + if cmd == "ping": + update_time() + await send_message(ws, "Pong!", room_id) + + elif cmd == "describe": + if len(args) >= 1: + update_time() + await gif_describe(args[0], room_id) + + elif cmd == "wins": + if len(args) >= 1: + update_time() + await gif_wins(args[0], room_id) + + elif cmd == "numbers": + update_time() + await gif_numbers(None, room_id) + + elif cmd == "date": + update_time() + await gif_date(None, room_id) + +async def gif_describe(who, room_id): + command = [ + gifmaker, + gm_common, + "--input 'describe.jpg'", + f"--words '{who} is [Random] [x5]' --bgcolor 0,0,0", + "--top 0 --fontsize 2.3 --filter random2", + ] + + await run_command(command, room_id) + +async def gif_wins(who, room_id): + command = [ + gifmaker, + gm_common, + "--input 'wins.gif'", + f"--words '{who} wins a ; [repeat] ; [RANDOM] ; [repeat]' --bgcolor 0,0,0 --boldness 2", + "--bottom 0 --fontsize 1.4 --boldness 2 --filter anyhue2 --framelist 11,11,33,33", + ] + + await run_command(command, room_id) + +async def gif_numbers(who, room_id): + command = [ + gifmaker, + gm_common, + "--input 'numbers.png'", + "--top 0 --words '[number 1-3] [x3]' --fontcolor 0,0,0", + ] + + await run_command(command, room_id) + +async def gif_date(who, room_id): + command = [ + gifmaker, + gm_common, + "--input 'time.jpg'", + "--words 'Date: [date %A %d] ; [repeat] ; Time: [date %I:%M %p] ; [repeat]'", + "--filter anyhue2 --bottom 0 --bgcolor 0,0,0", + ] + + await run_command(command, room_id) + +async def run_command(command, room_id): + process = await asyncio.create_subprocess_shell( + " ".join(command), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + ) + + stdout, stderr = await process.communicate() + + if process.returncode != 0: + print(f"(Process) Error: {stderr.decode()}") + return + + await upload(Path(stdout.decode().strip()), room_id) + +async def upload(path, room_id): + if not path.exists() or path.is_dir(): + return + + cookies = { + "session_id": session.split("=")[1], + "api_token": token.split("=")[1], + } + + ext = get_extension(path) + + files = { + "text": (None, ""), + "files[]": (path.name, open(path, "rb").read(), f"image/{ext}"), + } + + def remove_file(): + try: + path.unlink() + except Exception as e: + print(f"(Remove) Error: {e}") + traceback.print_exc() + + try: + requests.post("https://deek.chat/message/send/" + str(room_id), cookies=cookies, headers={}, files=files) + remove_file() + except Exception as e: + remove_file() + print(f"(Upload) Error: {e}") + traceback.print_exc() + +async def send_message(ws, text, room_id): + await ws.send(json.dumps({"type": "message", "data": text, "roomId": room_id})) + +while True: + try: + auth() + print("Authenticated") + asyncio.run(run()) + except KeyboardInterrupt: + break + except Exception as e: + print("(Main) Error:", e) + traceback.print_exc() \ No newline at end of file diff --git a/numbers.png b/numbers.png new file mode 100644 index 0000000..7145807 Binary files /dev/null and b/numbers.png differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..323c18e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests ~= 2.31.0 +websockets ~= 12.0 \ No newline at end of file diff --git a/time.jpg b/time.jpg new file mode 100644 index 0000000..9954f27 Binary files /dev/null and b/time.jpg differ diff --git a/wins.gif b/wins.gif new file mode 100644 index 0000000..32f11d6 Binary files /dev/null and b/wins.gif differ