diff --git a/async-bot.py b/async-bot.py index fa2fc72..b3a2545 100644 --- a/async-bot.py +++ b/async-bot.py @@ -1,5 +1,5 @@ import asyncio -from typing import NewType, Optional +from typing import Callable, NewType, Optional from socketio import AsyncClient, AsyncSimpleClient from aiohttp import ClientSession from aiohttp_socks import ProxyConnector @@ -8,6 +8,7 @@ from base64 import b64decode from random import choice, randint from json import load from time import time as time_now +import datetime PixelMap = NewType("PixelMap", dict[int, bool]) @@ -29,6 +30,8 @@ class AsyncBotManager: self.avoid: set[int] = set() self.animations: list[Animation] = [] + self.animation_functions: list[Callable[[], PixelMap]] = [] + self._written_boxes = 0 self._read_boxes = 0 self._last_printout = time_now() @@ -38,10 +41,11 @@ class AsyncBotManager: @staticmethod def get_text_image(text: str, font: ImageFont.ImageFont | ImageFont.FreeTypeFont) -> Image.Image: left, top, right, bottom = font.getbbox(text) - with Image.new("LA", (int(right - left) + 4, int(bottom - top) + 4), 0) as im: + with Image.new("LA", (int(right - left) + 4, int(bottom - top) + 8), 0) as im: draw = ImageDraw.Draw(im) draw.rectangle((0, 0, im.width, im.height), (0, 0)) - draw.text((left + 2, top + 2), text, font=font, fill=(255, 0)) + draw.text((left + 2, top + 2), text, font=font, fill=(255, 0), + anchor="lt") alpha = im.convert("L").filter(ImageFilter.MaxFilter(5)) im.putalpha(alpha) @@ -60,6 +64,17 @@ class AsyncBotManager: def put_text(self, x: int, y: int, text: str, font: str, size: int = 8): self.put_image(x, y, self.get_text_image(text, self.get_font(font, size))) + + def get_image_diff(self, ox: int, oy: int, im: Image.Image) -> PixelMap: + pixmap = PixelMap({}) + for y in range(im.height): + for x in range(im.width): + l, a = im.getpixel((x, y)) # type: ignore + index = x + ox + (y + oy) * 1000 + if a and index not in self.avoid: + pixmap[index] = l > 0 + return pixmap + def put_image(self, ox: int, oy: int, im: Image.Image): for y in range(im.height): for x in range(im.width): @@ -174,6 +189,10 @@ class AsyncBotManager: print(f"I/O: {incoming:7.2f}/s | {outgoing:7.2f}/s") print(f"Alive workers: {len(self._active)}") + if len(self._active) < 5: + self._shutdown = True + return + n_correct, n_wrong = 0, 0 for index, expected in self.difference.items(): y, x = divmod(index, 1000) @@ -193,6 +212,10 @@ class AsyncBotManager: self.difference.update( pixmaps[frame_index % len(pixmaps)] ) + + for func in self.animation_functions: + self.difference.update(func()) + except Exception as e: print(f"Listener died: {e!r}") self._shutdown = True @@ -208,22 +231,21 @@ class AsyncBotManager: async with ClientSession(connector=proxy) as http: async with AsyncSimpleClient(http_session=http) as sio: await sio.connect(f"{self.base}/socket.io") - offset = randint(0, 1000000) + offset = 0 while not self._shutdown: diff = list(self.difference.items()) + diff = sorted(diff, key=lambda kv: kv[0]) for _ in range(100): index, expected = diff[offset % len(diff)] - offset += randint(0, 100) + offset += randint(0, 1000) y, x = divmod(index, 1000) current = self.canvas.getpixel((x, y)) > 0 # type: ignore if current != expected: - # print(f"[{bot_index:2d}] swap {x:3d} {y:3d}") self._written_boxes += 1 await sio.emit("toggle_bit", {"index": index}) await asyncio.sleep(delay) - break - self._active.add(bot_index) - await asyncio.sleep(0.01) + self._active.add(bot_index) + await sio.receive(0.1) async def __aenter__(self): self._listener_task = asyncio.create_task(self.listener()) @@ -277,13 +299,27 @@ async def amain() -> None: mgr.put_image(elem["x"], elem["y"], im) print("ADD image", elem) + elif elem["type"] == "time": + time_format = elem["format"] + pos_x, pos_y = elem["x"], elem["y"] + font = mgr.get_font(elem.get("font", "default"), elem.get("size", 8)) + def update() -> PixelMap: + now = datetime.datetime.now(datetime.timezone.utc) + txt = now.strftime(time_format) + img = mgr.get_text_image(txt, font) + pixmap = mgr.get_image_diff(pos_x, pos_y, img) + img.close() + return pixmap + mgr.animation_functions.append(update) elif elem["type"] == "tile": with Image.open(elem["path"]).convert("LA") as im: - for i in range(elem.get("ry", 1)): - for j in range(elem.get("rx", 1)): - x = elem["x"] + im.width * j - y = elem["y"] + im.height * i - mgr.put_image(x, y, im) + for oy in range(elem.get("h", im.height)): + for ox in range(elem.get("w", im.width)): + l, a = im.getpixel((ox % im.width, oy % im.height)) # type: ignore + if a: + x, y = elem["x"] + ox, elem["y"] + oy + index = x + y * 1000 + mgr.put_bit(index, l > 0) print("ADD tile", elem) elif elem["type"] == "animation": with Image.open(elem["path"]) as anim: @@ -371,7 +407,9 @@ async def amain() -> None: ) for ret in res: - print(ret) + print("RETURN", repr(ret)) + + mgr._shutdown = True if __name__ == "__main__": diff --git a/pictures/casey.png b/pictures/casey.png index f226bae..4588c3b 100644 Binary files a/pictures/casey.png and b/pictures/casey.png differ diff --git a/pictures/hello.png b/pictures/hello.png new file mode 100644 index 0000000..cf36c03 Binary files /dev/null and b/pictures/hello.png differ diff --git a/pictures/mogus.png b/pictures/mogus.png new file mode 100644 index 0000000..427cac2 Binary files /dev/null and b/pictures/mogus.png differ diff --git a/pictures/niko_standing.png b/pictures/niko_standing.png new file mode 100644 index 0000000..45226ed Binary files /dev/null and b/pictures/niko_standing.png differ diff --git a/pictures/os_boat.png b/pictures/os_boat.png new file mode 100644 index 0000000..298e54d Binary files /dev/null and b/pictures/os_boat.png differ diff --git a/settings.json b/settings.json index 51a18ca..261c9ea 100644 --- a/settings.json +++ b/settings.json @@ -35,14 +35,6 @@ "stop": 1000000, "description": "catgirls.win text (both b64 and plain)" }, - { - "type": "rect", - "x": 400, - "y": 750, - "w": 400, - "h": 128, - "description": "'be gay do crime' sign" - }, { "type": "image", "path": "./avoid_masks/noita.png", @@ -50,16 +42,25 @@ } ], "elements": [ + { + "type": "time", + "x": 75, + "y": 100, + "format": "And time is: %Y-%m-%d %H:%M:%S UTC", + "spf": 20, + "font": "/usr/share/fonts/TTF/TerminusTTF.ttf", + "size": 12 + }, { "type": "image", "path": "./pictures/casey.png", - "x": 32, - "y": 420 + "x": 0, + "y": 128 }, { "type": "rgb565", "path": "./pictures/niko_standing.png", - "x": 106, + "x": 116, "y": 132 }, { @@ -67,6 +68,21 @@ "path": "./pictures/hello.png", "x": 112, "y": 203 + }, + { + "type": "text", + "font": "/usr/share/fonts/TTF/comic.ttf", + "size": 20, + "x": 250, + "y": 492, + "text": "You like kissing, don't you?" + }, + { + "type": "animation", + "path": "./pictures/neko.gif", + "spf": 30, + "x": 625, + "y": 496 } ] }