first working version
Marco Andronaco andronacomarco@gmail.com
Mon, 05 Sep 2022 04:17:59 +0200
6 files changed,
220 insertions(+),
78 deletions(-)
M
.gitignore
→
.gitignore
@@ -1,2 +1,6 @@
+.env venv output_image.jpg +__pycache__ +.ipynb_checkpoints +output.jpg
A
Api.py
@@ -0,0 +1,51 @@
+ +from PIL import Image +from io import BytesIO +import requests, random, time + +url = "https://danbooru.donmai.us/post/index.json" +test_url = "https://testbooru.donmai.us/post/index.json" + +ratings = [ + 'g', # general + 's', # sensitive + 'q', # questionable + 'e', # explicit +] + +params = { + "limit": 100, + "page": random.randint(1,560), + #"page": 560, + "tags": "order:change_desc rating:" +} + +def get_random_image(rating="g,s"): + params["tags"] += rating + + switch = True + while switch: + r = requests.get(url, params) + page = r.json() + print("Page: " + str(params['page'])) + + if 'success' in page: + if page['success'] == False: + print("Error: " + page['error']) + print("Message: " + page['message']) + print("Retrying in 3 seconds...\n") + else: + print(page) + time.sleep(3) + else: + n = random.randint(0, 99) + print("File: " + str(n)) + file_url = page[n]['file_url'] + r = requests.get(file_url) + try: + img = Image.open(BytesIO(r.content)) + except UnidentifiedImageError: + print("Unidentified image.") + + print("Done.\n") + return img, f"https://danbooru.donmai.us/posts/{page[n]['id']}"
A
Effects.py
@@ -0,0 +1,81 @@
+from PIL import Image, ImageDraw, ImageFont +import textwrap, os + +IMPACT_FONT_FILE = os.path.join("fonts", "impact.ttf") +BASE_WIDTH = 1200 +LETTER_SPACING = 9 +LINE_SPACING = 10 +FILL = (255, 255, 255) +STROKE_WIDTH = 9 +STROKE_FILL = (0, 0, 0) +FONT_BASE = 100 +MARGIN = 10 + +def _draw_tt_bt(text, img, bottom=False): + split_caption = textwrap.wrap(text.upper(), width=20) + if split_caption == []: + return + font_size = FONT_BASE + 10 if len(split_caption) <= 1 else FONT_BASE + font = ImageFont.truetype(font=IMPACT_FONT_FILE, size=font_size) + img_width, img_height = img.size + + d = ImageDraw.Draw(img) + txt_height = d.textbbox((0, 0), split_caption[0], font=font)[3] + + if bottom: + factor = -1 + split_caption.reverse() + y = (img_height - (img_height / MARGIN)) - (txt_height / 2) + else: + factor = 1 + y = ((img_height / MARGIN)) - (txt_height / 1.5) + + for line in split_caption: + txt_width = d.textbbox((0, 0), line, font=font)[2] + + x = (img_width - txt_width - (len(line) * LETTER_SPACING))/2 + + for i in range(len(line)): + char = line[i] + width = font.getlength(char) + d.text((x, y), char, fill=FILL, stroke_width=STROKE_WIDTH, font=font, stroke_fill=STROKE_FILL) + x += width + LETTER_SPACING + + y = y + (txt_height + LINE_SPACING) * factor + +def tt_bt(text, img): + lines = [x for x in text.split("\n") if x] + + tt = lines[0] if len(lines) > 0 else None + bt = lines[1] if len(lines) > 1 else None + + img = img.resize((BASE_WIDTH, int(img.size[1] * float(BASE_WIDTH / img.size[0])))) + + if tt is None and bt is None: + return img + + if (tt is not None): + _draw_tt_bt(tt, img) + if (bt is not None): + _draw_tt_bt(bt, img, bottom=True) + + h_size = int(float(img.size[1]) * (BASE_WIDTH/2) / img.size[0]) + img = img.resize((int(BASE_WIDTH/2), h_size)) + + if img.mode in ("RGBA", "P"): + img = img.convert("RGB") + + return img + +def image_test(): + image = Image.open("image.jpg") + #image, url = get_random_image() + + res = tt_bt("top text\nbottom text", image) + res.save('./output.jpg', optimize=True, quality=80) + + print("Image test successful") + +if __name__ == "__main__": + #main() + image_test()
M
main.py
→
main.py
@@ -1,90 +1,95 @@
-from PIL import Image, ImageDraw, ImageFont -import textwrap +from PIL import Image +from Api import get_random_image +from Effects import tt_bt -class Meme: +from dotenv import load_dotenv +load_dotenv() +import os, logging +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) +from io import BytesIO - basewidth = 1200 #Width to make the meme - fontBase = 100 - letSpacing = 9 #Space between letters - fill = (255, 255, 255) #TextColor - stroke_fill = (0,0,0) #outlineColor - lineSpacing = 10 #Space between lines - stroke_width=9 #How thick the outline of the text is - fontfile = './impact.ttf' +from telegram.ext import Updater, CallbackContext, CommandHandler, MessageHandler, Filters +from telegram import Update - def __init__(self, caption, image): - self.img = self.createImage(image) - self.d = ImageDraw.Draw(self.img) +sauce_str = "[Sauce 🍝]({})" - self.splitCaption = textwrap.wrap(caption.upper(), width=20) # The text can be wider than the img. If thats the case split the text into multiple lines - self.splitCaption.reverse() # Draw the lines of text from the bottom up +def _ttbt_general(text): + image, url = get_random_image() + image = tt_bt(text, image) + bio = BytesIO() + bio.name = 'image.jpeg' + image.save(bio, 'JPEG') + bio.seek(0) + return bio, sauce_str.format(url) - fontSize = self.fontBase+10 if len(self.splitCaption) <= 1 else self.fontBase #If there is only one line, make the text a bit larger - self.font = ImageFont.truetype(font=self.fontfile, size=fontSize) - # self.shadowFont = ImageFont.truetype(font='./impact.ttf', size=fontSize+10) +def _get_reply(input, fallback=""): + if input is None: + return fallback + return input.text - def draw(self): - ''' - Draws text onto this objects img object - :return: A pillow image object with text drawn onto the image - ''' - (iw, ih) = self.img.size - (_, th) = self.d.textsize(self.splitCaption[0], font=self.font) #Height of the text - y = (ih - (ih / 10)) - (th / 2) #The starting y position to draw the last line of text. Text in drawn from the bottom line up +def _get_image(): + image, url = get_random_image() + bio = BytesIO() + bio.name = 'image.jpeg' + image.save(bio, 'JPEG') + bio.seek(0) + return bio, sauce_str.format(url) - for cap in self.splitCaption: #For each line of text - (tw, _) = self.d.textsize(cap, font=self.font) # Getting the position of the text - x = ((iw - tw) - (len(cap) * self.letSpacing))/2 # Center the text and account for the spacing between letters +def start(update: Update, context: CallbackContext): + context.bot.send_message(chat_id=update.effective_chat.id, text="Welcome to PILuAnimeBot!") - self.drawLine(x=x, y=y, caption=cap) - y = y - th - self.lineSpacing # Next block of text is higher up +def pic(update: Update, context: CallbackContext): + image, url = _get_image() + + context.bot.send_photo(chat_id=update.effective_chat.id, photo=image, caption=url, parse_mode="markdown") - wpercent = ((self.basewidth/2) / float(self.img.size[0])) - hsize = int((float(self.img.size[1]) * float(wpercent))) - return self.img.resize((int(self.basewidth/2), hsize)) +def tt(update: Update, context: CallbackContext): + reply = _get_reply(update.message.reply_to_message) + content = ' '.join(context.args) + input_text = f"{reply} {content}".replace("\n", " ") + + image, url = _ttbt_general(input_text) + context.bot.send_photo(update.effective_chat.id, photo=image, caption=url, parse_mode="markdown") - def createImage(self, image): - ''' - Resizes the image to a resonable standard size - :param image: Path to an image file - :return: A pil image object - ''' - img = Image.open(image) - wpercent = (self.basewidth / float(img.size[0])) - hsize = int((float(img.size[1]) * float(wpercent))) - return img.resize((self.basewidth, hsize)) +def bt(update: Update, context: CallbackContext): + reply = _get_reply(update.message.reply_to_message) + content = ' '.join(context.args) + input_text = f"{reply} {content}".replace("\n", " ") + + image, url =_ttbt_general(" \n" + input_text) + context.bot.send_photo(update.effective_chat.id, photo=image, caption=url, parse_mode="markdown") - def drawLine(self, x, y, caption): - ''' - The text gets split into multiple lines if it is wider than the image. This function draws a single line - :param x: The starting x coordinate of the text - :param y: The starting y coordinate of the text - :param caption: The text to write on the image - :return: None - ''' - for idx in range(0, len(caption)): #For each letter in the line of text - char = caption[idx] - w, h = self.font.getsize(char) #width and height of the letter - self.d.text( - (x, y), - char, - fill=self.fill, - stroke_width=self.stroke_width, - font=self.font, - stroke_fill=self.stroke_fill - ) # Drawing the text character by character. This way spacing can be added between letters - x += w + self.letSpacing #The next character must be drawn at an x position more to the right +def ttbt(update: Update, context: CallbackContext): + reply = _get_reply(update.message.reply_to_message) + content = ' '.join(context.args) + input_text = f"{reply}\n{content}" + + image, url =_ttbt_general(input_text) + context.bot.send_photo(update.effective_chat.id, photo=image, caption=url, parse_mode="markdown") + +def caps(update: Update, context: CallbackContext): + reply = _get_reply(update.message.reply_to_message, ' '.join(context.args)) + context.bot.send_message(chat_id=update.effective_chat.id, text=reply.upper()) + +def unknown(update: Update, context: CallbackContext): + context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.") - - -caption = '''Quando io e poi lei e poi io e poi lei e poi +def main(): + + updater = Updater(token=os.getenv("token")) + dispatcher = updater.dispatcher + + dispatcher.add_handler(CommandHandler('start', start)) + dispatcher.add_handler(CommandHandler('caps', caps)) + dispatcher.add_handler(CommandHandler('pic', pic)) + dispatcher.add_handler(CommandHandler('ttbt', ttbt)) + dispatcher.add_handler(CommandHandler('tt', tt)) + dispatcher.add_handler(CommandHandler('bt', bt)) + + dispatcher.add_handler(MessageHandler(Filters.command, unknown)) + + updater.start_polling() + updater.idle() -Bottom text''' -image = './image.jpg' -outputImage = './output_image.jpg' - -meme = Meme(caption, image) -img = meme.draw() -if img.mode in ("RGBA", "P"): #Without this the code can break sometimes - img = img.convert("RGB") -img.save(outputImage, optimize=True, quality=80) #Save with some image optimization +if __name__ == "__main__": + main()
M
requirements.txt
→
requirements.txt
@@ -1,2 +1,3 @@
Pillow -output_image.jpg+python-telegram-bot +python-dotenv