import calendar import logging import os import random import textwrap import time import httpx from icrawler.builtin import BingImageCrawler from PIL import Image, ImageDraw, ImageEnhance, ImageFont, ImageOps from unidecode import unidecode from .formatter import format_text, limit_per_page def convert_to_png(image: str) -> str: output_img = f"png_{round(time.time())}.png" img = Image.open(image) img.save(output_img, "PNG") img.close() os.remove(image) return output_img def add_rounded_corners(img: Image.Image, radius: int = 80): circle = Image.new("L", (radius * 2, radius * 2), 0) draw = ImageDraw.Draw(circle) draw.ellipse((0, 0, radius * 2, radius * 2), fill=255) alpha = Image.new("L", img.size, 255) w, h = img.size alpha.paste(circle.crop((0, 0, radius, radius)), (0, 0)) alpha.paste(circle.crop((radius, 0, radius * 2, radius)), (w - radius, 0)) alpha.paste(circle.crop((0, radius, radius, radius * 2)), (0, h - radius)) alpha.paste( circle.crop((radius, radius, radius * 2, radius * 2)), (w - radius, h - radius) ) img.putalpha(alpha) return img def generate_alive_image( username: str, profile_pic: str, del_img: bool, font_path: str ) -> str: if not profile_pic.endswith(".png"): profile_pic = convert_to_png(profile_pic) img = Image.open(profile_pic).convert("RGBA") img_rotated = img.rotate(45, expand=True) width, height = img_rotated.size left = width / 2 - 480 / 2 top = height / 2 - 480 / 2 right = width / 2 + 480 / 2 bottom = height / 2 + 480 / 2 cropped_img = img_rotated.crop((left, top, right, bottom)) img_rotated = ImageOps.fit( cropped_img, (480, 480), method=0, bleed=0.0, centering=(0.5, 0.5) ) img_rounded = add_rounded_corners(img_rotated) img = img_rounded.rotate(-45, expand=True) background = Image.open("./Hellbot/resources/images/hellbot_alive.png").convert( "RGBA" ) background.paste(img, (383, 445), img) draw = ImageDraw.Draw(background) text = format_text(username[:25] + ("..." if len(username) > 25 else "")) font_size = width // 15 font = ImageFont.truetype(font_path, font_size, encoding="utf-8") text_length = draw.textlength(text, font) position = ((background.width - text_length) / 2, background.height - 145) draw.text( position, unidecode(text), (255, 255, 255), font, ) output_img = f"alive_{int(time.time())}.png" background.save(output_img, "PNG") background.close() if del_img: os.remove(profile_pic) return output_img async def get_wallpapers( access: str, limit: int, query: str = "", isRandom: bool = False, ) -> list[str]: headers = {"Authorization": f"Client-ID {access}"} if isRandom: api = f"https://api.unsplash.com/photos/random?count={limit}" response = httpx.get(api, headers=headers) results = response.json() urls = [i["urls"]["raw"] for i in results] else: api = f"https://api.unsplash.com/search/photos?query={query}&page={limit_per_page(limit)}" response = httpx.get(api, headers=headers) result = response.json() urls = [i["urls"]["raw"] for i in result["results"]] random.shuffle(urls) return urls[:limit] async def deep_fry(img: Image.Image) -> Image.Image: colours = ( (random.randint(50, 200), random.randint(40, 170), random.randint(40, 190)), (random.randint(190, 255), random.randint(170, 240), random.randint(180, 250)), ) img = img.copy().convert("RGB") img = img.convert("RGB") width, height = img.width, img.height img = img.resize( ( int(width ** random.uniform(0.8, 0.9)), int(height ** random.uniform(0.8, 0.9)), ), resample=Image.LANCZOS, ) img = img.resize( ( int(width ** random.uniform(0.85, 0.95)), int(height ** random.uniform(0.85, 0.95)), ), resample=Image.BILINEAR, ) img = img.resize( ( int(width ** random.uniform(0.89, 0.98)), int(height ** random.uniform(0.89, 0.98)), ), resample=Image.BICUBIC, ) img = img.resize((width, height), resample=Image.BICUBIC) img = ImageOps.posterize(img, random.randint(3, 7)) overlay = img.split()[0] overlay = ImageEnhance.Contrast(overlay).enhance(random.uniform(1.0, 2.0)) overlay = ImageEnhance.Brightness(overlay).enhance(random.uniform(1.0, 2.0)) overlay = ImageOps.colorize(overlay, colours[0], colours[1]) img = Image.blend(img, overlay, random.uniform(0.1, 0.4)) img = ImageEnhance.Sharpness(img).enhance(random.randint(5, 300)) return img async def make_logo(background: str, text: str, font_path: str) -> str: if not background.endswith(".png"): background = convert_to_png(background) bg = Image.open(background).convert("RGBA") bgWidth, bgHeight = bg.size text = format_text(text) font_size = bgWidth // len(text) font = ImageFont.truetype(font_path, font_size, encoding="utf-8") draw = ImageDraw.Draw(bg) text_length = draw.textlength(text, font) x = (bgWidth - text_length) // 2 y = (bgHeight - font_size) // 2 draw.text( (x, y), unidecode(text), (255, 255, 255), font, stroke_fill=(0, 0, 0), stroke_width=2, ) output_img = f"logo_{int(time.time())}.png" bg.save(output_img, "PNG") bg.close() os.remove(background) return output_img async def draw_meme( image_path: str, upper_text: str = "", lower_text: str = "" ) -> list[str]: image = Image.open(image_path) width, height = image.size draw = ImageDraw.Draw(image) font_size = int((30 / 500) * width) font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", font_size) curr_height, padding = 20, 5 for utext in textwrap.wrap(upper_text, 25): upper_width = draw.textlength(utext, font=font) draw.text( ((width - upper_width) / 2, curr_height), unidecode(utext), (255, 255, 255), font, stroke_width=3, stroke_fill=(0, 0, 0), ) curr_height += font_size + padding curr_height = height - font_size for ltext in reversed(textwrap.wrap(lower_text, 25)): lower_width = draw.textlength(ltext, font=font) draw.text( ((width - lower_width) / 2, curr_height - font_size), ltext, (255, 255, 255), font, stroke_width=3, stroke_fill=(0, 0, 0), ) curr_height -= font_size + padding filename = f"meme_{int(time.time())}" image.save(f"{filename}.png", "PNG", optimize=True) image.save(f"{filename}.webp", "WEBP", optimize=True) image.close() return [f"{filename}.png", f"{filename}.webp"] async def remove_bg(api_key: str, image: str) -> str: response = httpx.post( "https://api.remove.bg/v1.0/removebg", files={"image_file": open(image, "rb")}, data={"size": "auto"}, headers={"X-Api-Key": api_key}, ) filename = f"removedbg_{int(time.time())}.png" if response.is_success: with open(filename, "wb") as f: f.write(response.content) else: raise Exception( f"RemoveBGError: [{response.status_code}] {response.content.decode('utf-8')}" ) return filename def create_gradient( size: tuple[int, int], color_start: tuple[int, int, int], color_end: tuple[int, int, int], ) -> Image.Image: gradient = Image.new("RGB", (size)) draw = ImageDraw.Draw(gradient) for x in range(size[0]): r = int(color_start[0] + (color_end[0] - color_start[0]) * (x / size[0])) g = int(color_start[1] + (color_end[1] - color_start[1]) * (x / size[0])) b = int(color_start[2] + (color_end[2] - color_start[2]) * (x / size[0])) draw.line([(x, 0), (x, size[1])], fill=(r, g, b)) return gradient async def create_calendar(year: int, month: int) -> str: cal = calendar.monthcalendar(year, month) month_name = calendar.month_name[month] calendar_image = create_gradient((500, 500), (140, 200, 250), (0, 150, 200)) draw = ImageDraw.Draw(calendar_image) month_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 40) month_x = ( calendar_image.width - draw.textlength(f"{month_name} {year}", month_font) ) // 2 month_y = 30 draw.text( (month_x, month_y), f"{month_name} {year}", (43, 255, 136), month_font, stroke_width=2, stroke_fill=(255, 40, 40), ) week_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 23) weekdays_text = " ".join([day[:3] for day in calendar.day_name]) textsize = draw.textlength(weekdays_text, week_font) draw.text( ((calendar_image.width - textsize) // 2, month_y + 80), weekdays_text, (150, 190, 200), week_font, stroke_width=2, stroke_fill=(200, 150, 250), ) scale_factor = 1.5 cell_size = 30 padding = 15 font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 30) for week_num, week in enumerate(cal): for day_num, day in enumerate(week): x = int(day_num * (cell_size + padding) * scale_factor) y = int((week_num + 3) * (cell_size + padding) * scale_factor) cell_width = int(cell_size * scale_factor) cell_height = int(cell_size * scale_factor) text_x = ( int(x + (cell_width - draw.textlength(str(day), font=font)) // 2) + cell_size ) text_y = ( int(y + (cell_height - draw.textlength(str(day), font=font)) // 2) - 55 ) if day != 0: draw.text( (text_x, text_y), str(day), (240, 200, 100), font, stroke_width=1, stroke_fill=(0, 0, 0), ) filename = f"calendar_{int(time.time())}.png" calendar_image.save(filename, "PNG") calendar_image.close() return filename async def create_thumbnail(photo: str, xy: tuple[int, int], file_size: int): img = Image.open(photo) img.thumbnail(xy) size_in_bytes = file_size * 1024 quality = 90 while True: img.save(photo, "JPEG", quality=quality, optimize=True) if os.path.getsize(photo) <= size_in_bytes: break quality -= 5 return photo async def download_images(query: str, limit: int) -> list[str]: offset = random.randint(0, 20) crawler = BingImageCrawler(log_level=logging.ERROR) crawler.crawl(query, offset=offset, max_num=limit) return [os.path.join("images", image) for image in os.listdir("images")]