2022-07-20 03:46:02 +00:00
|
|
|
import re
|
2022-07-20 02:48:32 +00:00
|
|
|
import time
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import json
|
|
|
|
from base64 import b64decode
|
|
|
|
from typing import Optional, List
|
|
|
|
from datetime import datetime
|
|
|
|
import logging
|
|
|
|
import asyncio
|
|
|
|
from io import BytesIO
|
|
|
|
import bottom
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
from PIL.JpegImagePlugin import JpegImageFile
|
|
|
|
import aiohttp
|
|
|
|
|
|
|
|
from settings import *
|
|
|
|
|
|
|
|
TASK_QUEUE = asyncio.Queue()
|
|
|
|
TASK_AUTHORS = {}
|
|
|
|
FONT = ImageFont.truetype('font-bold.ttf', 22)
|
|
|
|
bot = bottom.Client(host=IRC_HOST, port=IRC_PORT, ssl=IRC_TLS, loop=asyncio.get_event_loop())
|
|
|
|
|
|
|
|
USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36'
|
|
|
|
HEADERS = {
|
|
|
|
'User-Agent': USER_AGENT,
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Accept-Language': 'en-US,en;q=0.5',
|
|
|
|
'Referer': 'https://www.craiyon.com/',
|
|
|
|
'Origin': 'https://www.craiyon.com',
|
|
|
|
'Connection': 'keep-alive',
|
|
|
|
'Sec-Fetch-Dest': 'empty',
|
|
|
|
'Sec-Fetch-Mode': 'cors',
|
|
|
|
'Sec-Fetch-Site': 'same-site',
|
|
|
|
'Pragma': 'no-cache',
|
|
|
|
'Cache-Control': 'no-cache',
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class Task:
|
|
|
|
def __init__(self, author: str, channel: str, term: str, dt: int):
|
|
|
|
self.author = author
|
|
|
|
self.channel = channel
|
|
|
|
self.term = term
|
|
|
|
self.dt = dt
|
|
|
|
|
|
|
|
async def process_images(self, images: List[JpegImageFile]) -> JpegImageFile:
|
|
|
|
if len(images) <= 0:
|
|
|
|
raise Exception("Not enough images")
|
|
|
|
|
|
|
|
if MODE == "monero":
|
|
|
|
bg = Image.open('bg-pools.jpg')
|
|
|
|
term_max = 60
|
|
|
|
term_x_start = 100
|
|
|
|
else:
|
|
|
|
bg = Image.open('bg-wownero.jpg')
|
|
|
|
term_max = 52
|
|
|
|
term_x_start = 230
|
|
|
|
|
|
|
|
d1 = ImageDraw.Draw(bg)
|
|
|
|
|
|
|
|
term = self.term
|
|
|
|
if len(term) >= term_max:
|
|
|
|
term = self.term[:term_max] + ".."
|
|
|
|
|
|
|
|
d1.text((term_x_start, 160), term, font=FONT, fill=(255, 255, 255))
|
|
|
|
|
|
|
|
x_start = 41
|
|
|
|
y_start = 232
|
|
|
|
for i, img in enumerate(images):
|
|
|
|
x_offset = (i % 3) * (img.width + 20)
|
|
|
|
y_offset = int(i / 3) * (img.width + 20)
|
|
|
|
bg.paste(img, (x_start + x_offset, y_start + y_offset))
|
|
|
|
return bg
|
|
|
|
|
|
|
|
async def post_image(self, image: BytesIO) -> str:
|
|
|
|
url = 'https://uguu.se/upload.php'
|
|
|
|
timeout = aiohttp.ClientTimeout(total=5)
|
|
|
|
|
|
|
|
data = aiohttp.FormData()
|
|
|
|
data.add_field('files[]', image, filename='lol.jpg', content_type='image/jpg')
|
|
|
|
|
|
|
|
try:
|
|
|
|
async with aiohttp.ClientSession(
|
|
|
|
timeout=timeout,
|
|
|
|
headers={"User-Agent": "w0w"}) as session:
|
|
|
|
async with session.post(url, data=data) as resp:
|
|
|
|
blob = await resp.json()
|
|
|
|
return blob['files'][0]['url']
|
|
|
|
except Exception as ex:
|
|
|
|
raise Exception("Could not upload file to uguu.se :((")
|
|
|
|
|
|
|
|
async def get_images(self) -> Optional[List[JpegImageFile]]:
|
|
|
|
url = 'https://backend.craiyon.com/generate'
|
|
|
|
timeout_secs = 120
|
|
|
|
timeout = aiohttp.ClientTimeout(total=timeout_secs)
|
|
|
|
images = []
|
|
|
|
|
|
|
|
try:
|
|
|
|
async with aiohttp.ClientSession(
|
|
|
|
timeout=timeout,
|
|
|
|
headers=HEADERS) as session:
|
|
|
|
async with session.post(url, json={'prompt': self.term}) as resp:
|
|
|
|
if resp.status != 200:
|
|
|
|
raise Exception("POST did not return status 200")
|
|
|
|
blob = await resp.json()
|
|
|
|
if 'images' not in blob:
|
|
|
|
raise Exception("result did not return images")
|
|
|
|
images = blob['images']
|
|
|
|
except asyncio.TimeoutError:
|
|
|
|
raise Exception(f"image generation timeout exceeded ({timeout_secs} secs)")
|
|
|
|
|
|
|
|
try:
|
|
|
|
images = [b64decode(i) for i in images]
|
|
|
|
return [Image.open(BytesIO(i)) for i in images]
|
|
|
|
except Exception as ex:
|
|
|
|
raise Exception("could not decode results")
|
|
|
|
|
|
|
|
|
|
|
|
@bot.on('CLIENT_CONNECT')
|
|
|
|
async def connect(**kwargs):
|
|
|
|
bot.send('NICK', nick=IRC_NICK)
|
|
|
|
bot.send('USER', user=IRC_NICK, realname=IRC_NICK)
|
|
|
|
done, pending = await asyncio.wait(
|
|
|
|
[bot.wait("RPL_ENDOFMOTD"), bot.wait("ERR_NOMOTD")],
|
|
|
|
loop=bot.loop,
|
|
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
|
|
)
|
|
|
|
|
|
|
|
for future in pending:
|
|
|
|
future.cancel()
|
|
|
|
for c in IRC_CHANNELS:
|
|
|
|
bot.send('JOIN', channel=c)
|
|
|
|
|
|
|
|
|
|
|
|
@bot.on('PING')
|
|
|
|
def keepalive(message, **kwargs):
|
|
|
|
bot.send('PONG', message=message)
|
|
|
|
|
|
|
|
|
|
|
|
@bot.on('client_disconnect')
|
|
|
|
def reconnect(**kwargs):
|
|
|
|
logging.warning("Lost IRC server connection")
|
|
|
|
time.sleep(3)
|
|
|
|
bot.loop.create_task(bot.connect())
|
|
|
|
logging.warning("Reconnecting to IRC server")
|
|
|
|
|
|
|
|
|
|
|
|
@bot.on('PRIVMSG')
|
|
|
|
async def message_received(nick, target, message, **kwargs):
|
|
|
|
if nick == IRC_NICK:
|
|
|
|
return
|
|
|
|
if target == IRC_NICK:
|
|
|
|
target = nick
|
|
|
|
if target not in IRC_CHANNELS:
|
|
|
|
return
|
|
|
|
|
|
|
|
msg = message
|
2022-07-20 03:46:02 +00:00
|
|
|
msg = msg.replace("!dall", " !dall")
|
|
|
|
msg = re.sub(r'!\W+', ' ', msg)
|
2022-07-20 02:48:32 +00:00
|
|
|
now = datetime.now()
|
|
|
|
now = time.mktime(now.timetuple())
|
|
|
|
await handle_msg(nick, msg, target, now)
|
|
|
|
|
|
|
|
|
|
|
|
async def handle_msg(nick, msg, target, now: float):
|
|
|
|
global TASK_AUTHORS, TASK_QUEUE
|
2022-07-20 03:24:05 +00:00
|
|
|
|
|
|
|
def _err(target, err):
|
|
|
|
return bot.send("PRIVMSG", target=target, message=err)
|
|
|
|
|
|
|
|
if "!dall" not in msg:
|
2022-07-20 02:48:32 +00:00
|
|
|
return
|
|
|
|
|
2022-07-20 03:24:05 +00:00
|
|
|
spl = msg.split(' ')
|
2022-07-20 03:46:02 +00:00
|
|
|
if "!dall" not in spl[:3]: # stupid fix for IRC bridge bots
|
2022-07-20 02:48:32 +00:00
|
|
|
return
|
|
|
|
|
2022-07-20 03:24:05 +00:00
|
|
|
msg = msg[msg.find("!dall"):]
|
|
|
|
msg = msg[5:].strip()
|
|
|
|
|
|
|
|
if len(msg) <= 8:
|
|
|
|
return _err(target, f"longer query required.")
|
|
|
|
|
2022-07-20 02:48:32 +00:00
|
|
|
TASK_AUTHORS.setdefault(nick, 0)
|
|
|
|
if TASK_AUTHORS[nick] >= 3:
|
|
|
|
err = f"{nick}: you already queued 3 thingies, patient!!11"
|
|
|
|
bot.send("PRIVMSG", target=target, message=err)
|
|
|
|
return
|
|
|
|
|
|
|
|
TASK_AUTHORS[nick] += 1
|
|
|
|
task = Task(channel=target, author=nick, term=msg, dt=int(now))
|
|
|
|
await TASK_QUEUE.put(task)
|
|
|
|
bot.send("PRIVMSG", target=target, message=f"{nick}: generating...")
|
|
|
|
|
|
|
|
|
|
|
|
async def main():
|
|
|
|
global TASK_AUTHORS, TASK_QUEUE
|
|
|
|
bot.loop.create_task(bot.connect())
|
|
|
|
|
|
|
|
# main loop
|
|
|
|
while True:
|
|
|
|
random.shuffle(TASK_QUEUE._queue)
|
|
|
|
task = await TASK_QUEUE.get()
|
|
|
|
|
|
|
|
try:
|
|
|
|
images = await task.get_images()
|
|
|
|
except Exception as ex:
|
|
|
|
bot.send("PRIVMSG", target=task.channel, message=f"could not fetch images: {ex}")
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
image = await task.process_images(images)
|
|
|
|
except Exception as ex:
|
|
|
|
bot.send("PRIVMSG", target=task.channel, message=f"could not process images: {ex}")
|
|
|
|
continue
|
|
|
|
|
|
|
|
image_bytes = BytesIO()
|
|
|
|
image.save(image_bytes, 'JPEG')
|
|
|
|
image.save(os.path.join('data', f"{task.dt}_{task.author}.jpg"), 'JPEG')
|
|
|
|
image_bytes.seek(0)
|
|
|
|
|
|
|
|
try:
|
|
|
|
url = await task.post_image(image_bytes)
|
|
|
|
except Exception as ex:
|
|
|
|
bot.send("PRIVMSG", target=task.channel, message=str(ex))
|
|
|
|
continue
|
|
|
|
|
|
|
|
TASK_AUTHORS.setdefault(task.author, 1)
|
|
|
|
TASK_AUTHORS[task.author] -= 1
|
|
|
|
|
|
|
|
bot.send("PRIVMSG", target=task.channel, message=f"{url} ({task.author})")
|
|
|
|
await asyncio.sleep(10)
|
|
|
|
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
loop.run_until_complete(main())
|