parent
4a7f633a28
commit
319da9746f
66
async-bot.py
66
async-bot.py
|
@ -1,5 +1,5 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import NewType, Optional
|
from typing import Callable, NewType, Optional
|
||||||
from socketio import AsyncClient, AsyncSimpleClient
|
from socketio import AsyncClient, AsyncSimpleClient
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
from aiohttp_socks import ProxyConnector
|
from aiohttp_socks import ProxyConnector
|
||||||
|
@ -8,6 +8,7 @@ from base64 import b64decode
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from json import load
|
from json import load
|
||||||
from time import time as time_now
|
from time import time as time_now
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
PixelMap = NewType("PixelMap", dict[int, bool])
|
PixelMap = NewType("PixelMap", dict[int, bool])
|
||||||
|
@ -29,6 +30,8 @@ class AsyncBotManager:
|
||||||
self.avoid: set[int] = set()
|
self.avoid: set[int] = set()
|
||||||
|
|
||||||
self.animations: list[Animation] = []
|
self.animations: list[Animation] = []
|
||||||
|
self.animation_functions: list[Callable[[], PixelMap]] = []
|
||||||
|
|
||||||
self._written_boxes = 0
|
self._written_boxes = 0
|
||||||
self._read_boxes = 0
|
self._read_boxes = 0
|
||||||
self._last_printout = time_now()
|
self._last_printout = time_now()
|
||||||
|
@ -38,10 +41,11 @@ class AsyncBotManager:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_text_image(text: str, font: ImageFont.ImageFont | ImageFont.FreeTypeFont) -> Image.Image:
|
def get_text_image(text: str, font: ImageFont.ImageFont | ImageFont.FreeTypeFont) -> Image.Image:
|
||||||
left, top, right, bottom = font.getbbox(text)
|
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 = ImageDraw.Draw(im)
|
||||||
draw.rectangle((0, 0, im.width, im.height), (0, 0))
|
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))
|
alpha = im.convert("L").filter(ImageFilter.MaxFilter(5))
|
||||||
im.putalpha(alpha)
|
im.putalpha(alpha)
|
||||||
|
@ -60,6 +64,17 @@ class AsyncBotManager:
|
||||||
def put_text(self, x: int, y: int, text: str, font: str, size: int = 8):
|
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)))
|
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):
|
def put_image(self, ox: int, oy: int, im: Image.Image):
|
||||||
for y in range(im.height):
|
for y in range(im.height):
|
||||||
for x in range(im.width):
|
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"I/O: {incoming:7.2f}/s | {outgoing:7.2f}/s")
|
||||||
print(f"Alive workers: {len(self._active)}")
|
print(f"Alive workers: {len(self._active)}")
|
||||||
|
|
||||||
|
if len(self._active) < 5:
|
||||||
|
self._shutdown = True
|
||||||
|
return
|
||||||
|
|
||||||
n_correct, n_wrong = 0, 0
|
n_correct, n_wrong = 0, 0
|
||||||
for index, expected in self.difference.items():
|
for index, expected in self.difference.items():
|
||||||
y, x = divmod(index, 1000)
|
y, x = divmod(index, 1000)
|
||||||
|
@ -193,6 +212,10 @@ class AsyncBotManager:
|
||||||
self.difference.update(
|
self.difference.update(
|
||||||
pixmaps[frame_index % len(pixmaps)]
|
pixmaps[frame_index % len(pixmaps)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for func in self.animation_functions:
|
||||||
|
self.difference.update(func())
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Listener died: {e!r}")
|
print(f"Listener died: {e!r}")
|
||||||
self._shutdown = True
|
self._shutdown = True
|
||||||
|
@ -208,22 +231,21 @@ class AsyncBotManager:
|
||||||
async with ClientSession(connector=proxy) as http:
|
async with ClientSession(connector=proxy) as http:
|
||||||
async with AsyncSimpleClient(http_session=http) as sio:
|
async with AsyncSimpleClient(http_session=http) as sio:
|
||||||
await sio.connect(f"{self.base}/socket.io")
|
await sio.connect(f"{self.base}/socket.io")
|
||||||
offset = randint(0, 1000000)
|
offset = 0
|
||||||
while not self._shutdown:
|
while not self._shutdown:
|
||||||
diff = list(self.difference.items())
|
diff = list(self.difference.items())
|
||||||
|
diff = sorted(diff, key=lambda kv: kv[0])
|
||||||
for _ in range(100):
|
for _ in range(100):
|
||||||
index, expected = diff[offset % len(diff)]
|
index, expected = diff[offset % len(diff)]
|
||||||
offset += randint(0, 100)
|
offset += randint(0, 1000)
|
||||||
y, x = divmod(index, 1000)
|
y, x = divmod(index, 1000)
|
||||||
current = self.canvas.getpixel((x, y)) > 0 # type: ignore
|
current = self.canvas.getpixel((x, y)) > 0 # type: ignore
|
||||||
if current != expected:
|
if current != expected:
|
||||||
# print(f"[{bot_index:2d}] swap {x:3d} {y:3d}")
|
|
||||||
self._written_boxes += 1
|
self._written_boxes += 1
|
||||||
await sio.emit("toggle_bit", {"index": index})
|
await sio.emit("toggle_bit", {"index": index})
|
||||||
await asyncio.sleep(delay)
|
await asyncio.sleep(delay)
|
||||||
break
|
|
||||||
self._active.add(bot_index)
|
self._active.add(bot_index)
|
||||||
await asyncio.sleep(0.01)
|
await sio.receive(0.1)
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
self._listener_task = asyncio.create_task(self.listener())
|
self._listener_task = asyncio.create_task(self.listener())
|
||||||
|
@ -277,13 +299,27 @@ async def amain() -> None:
|
||||||
mgr.put_image(elem["x"], elem["y"], im)
|
mgr.put_image(elem["x"], elem["y"], im)
|
||||||
print("ADD image", elem)
|
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":
|
elif elem["type"] == "tile":
|
||||||
with Image.open(elem["path"]).convert("LA") as im:
|
with Image.open(elem["path"]).convert("LA") as im:
|
||||||
for i in range(elem.get("ry", 1)):
|
for oy in range(elem.get("h", im.height)):
|
||||||
for j in range(elem.get("rx", 1)):
|
for ox in range(elem.get("w", im.width)):
|
||||||
x = elem["x"] + im.width * j
|
l, a = im.getpixel((ox % im.width, oy % im.height)) # type: ignore
|
||||||
y = elem["y"] + im.height * i
|
if a:
|
||||||
mgr.put_image(x, y, im)
|
x, y = elem["x"] + ox, elem["y"] + oy
|
||||||
|
index = x + y * 1000
|
||||||
|
mgr.put_bit(index, l > 0)
|
||||||
print("ADD tile", elem)
|
print("ADD tile", elem)
|
||||||
elif elem["type"] == "animation":
|
elif elem["type"] == "animation":
|
||||||
with Image.open(elem["path"]) as anim:
|
with Image.open(elem["path"]) as anim:
|
||||||
|
@ -371,7 +407,9 @@ async def amain() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
for ret in res:
|
for ret in res:
|
||||||
print(ret)
|
print("RETURN", repr(ret))
|
||||||
|
|
||||||
|
mgr._shutdown = True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 802 B |
Binary file not shown.
After Width: | Height: | Size: 491 B |
Binary file not shown.
After Width: | Height: | Size: 991 B |
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -35,14 +35,6 @@
|
||||||
"stop": 1000000,
|
"stop": 1000000,
|
||||||
"description": "catgirls.win text (both b64 and plain)"
|
"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",
|
"type": "image",
|
||||||
"path": "./avoid_masks/noita.png",
|
"path": "./avoid_masks/noita.png",
|
||||||
|
@ -50,16 +42,25 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"elements": [
|
"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",
|
"type": "image",
|
||||||
"path": "./pictures/casey.png",
|
"path": "./pictures/casey.png",
|
||||||
"x": 32,
|
"x": 0,
|
||||||
"y": 420
|
"y": 128
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "rgb565",
|
"type": "rgb565",
|
||||||
"path": "./pictures/niko_standing.png",
|
"path": "./pictures/niko_standing.png",
|
||||||
"x": 106,
|
"x": 116,
|
||||||
"y": 132
|
"y": 132
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -67,6 +68,21 @@
|
||||||
"path": "./pictures/hello.png",
|
"path": "./pictures/hello.png",
|
||||||
"x": 112,
|
"x": 112,
|
||||||
"y": 203
|
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue