From 1e0eca40f635dad554367ab836a63ed9338c5bba Mon Sep 17 00:00:00 2001 From: yihong0618 Date: Wed, 3 Jul 2024 21:19:08 +0800 Subject: [PATCH] feat: fake bot Signed-off-by: yihong0618 --- README.md | 7 +- handlers/fake_liuneng.py | 188 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 handlers/fake_liuneng.py diff --git a/README.md b/README.md index 2eced2d..90b32c4 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,14 @@ for yihong0618's channel: https://t.me/hyi0618 ![image](https://github.com/yihong0618/tg_bot_collections/assets/15976103/29848d22-5289-4953-8ab0-4e84c16f79e3) +## Bot -> fake +1. install font wqy-microhei first +2. use `fake: ${message}` to generate ## Bot -> [ChatTTS](https://github.com/2noise/ChatTTS) -2. export USE_CHATTTS=true -3. use `tts: ${message}` to generate +1. export USE_CHATTTS=true +2. use `tts: ${message}` to generate ## Bot -> Gemini player diff --git a/handlers/fake_liuneng.py b/handlers/fake_liuneng.py new file mode 100644 index 0000000..afe8252 --- /dev/null +++ b/handlers/fake_liuneng.py @@ -0,0 +1,188 @@ +import random +from PIL import Image, ImageDraw, ImageFont +from os import listdir +from telebot import TeleBot +from telebot.types import Message +import re + +from . import * + + +def split_lines(text, max_length=18): + def split_line(line): + punctuation = r"[,.!?;,。!?;]" + parts = re.split(f"({punctuation})", line) + + result = [] + current = "" + + for part in parts: + if len(current) + len(part) <= max_length: + current += part + else: + if current: + result.append(current.strip()) + + while len(part) > max_length: + result.append(part[:max_length]) + part = part[max_length:] + + current = part + + if current: + result.append(current.strip()) + + return result + + lines = text.split("\n") + final_result = [] + for line in lines: + final_result.extend(split_line(line)) + + return final_result + + +def extract_prompt(message: str, bot_name: str) -> str: + """ + This function filters messages for prompts. + + Returns: + str: If it is not a prompt, return None. Otherwise, return the trimmed prefix of the actual prompt. + """ + # remove '@bot_name' as it is considered part of the command when in a group chat. + message = re.sub(re.escape(f"@{bot_name}"), "", message).strip() + # add a whitespace after the first colon as we separate the prompt from the command by the first whitespace. + message = re.sub(":", ": ", message, count=1).strip() + try: + left, message = message.split(maxsplit=1) + except ValueError: + return "" + if ":" not in left: + # the replacement happens in the right part, restore it. + message = message.replace(": ", ":", 1) + return message.strip() + + +class ImageRenderer: + def __init__(self): + self.canvas_width = 512 + self.quotes = [ + "我敬佩两种人\n年轻时陪男人过苦日子的女人\n富裕时陪女人过好日子的男人", + "人生就像一杯茶\n不会苦一辈子\n但总会苦一阵子", + "不要总拿自己跟别人比\n你羡慕别人瘦\n别人还羡慕你肠胃好\n你羡慕别人有钱\n别人还羡慕没人找你借钱", + "彪悍的人生不需要解释\n只要你按时达到目的地\n很少有人在乎你开的是奔驰还是拖拉机", + "如果你不够优秀\n人脉是不值钱的\n它不是追求来的\n而是吸引来的\n只有等价的交换\n才能得到合理的帮助\n虽然听起来很冷\n但这是事实", + "喜欢在你背后说三道四\n捏造故事的人\n无非就三个原因\n没达到你的层次\n你有的东西他没有\n模仿你的生活方式未遂", + "做一个特别简单的人\n好相处就处\n不好相处就不处\n不要一厢情愿去迎合别人\n你努力合群的样子并不漂亮\n不必对每个人好\n他们又不给你打钱", + ] + + def render_image(self, image_path, text): + image = Image.open(image_path) + scale_factor = self.canvas_width / image.width + scaled_height = int(image.height * scale_factor) + line_height = 50 + font_size = 20 + image_line_height = int(line_height / scale_factor) + lines = split_lines(text) + canvas_height = scaled_height + if len(lines) > 1: + canvas_height += (len(lines) - 1) * line_height + + canvas = Image.new("RGB", (self.canvas_width, canvas_height)) + canvas.paste(image.resize((self.canvas_width, scaled_height))) + + draw = ImageDraw.Draw(canvas) + # font = ImageFont.load_default() + font = ImageFont.truetype("wqy-microhei.ttc", font_size) + + for i, line in enumerate(lines): + if i > 0: + bottom_strip = image.crop( + (0, image.height - image_line_height, image.width, image.height) + ) + canvas.paste( + bottom_strip.resize((self.canvas_width, line_height)), + (0, scaled_height + (i - 1) * line_height), + ) + + y = scaled_height + i * line_height - (line_height - font_size) // 2 + draw.text( + (self.canvas_width // 2, y), + line, + fill="white", + font=font, + anchor="mm", + stroke_width=2, + stroke_fill="black", + ) + + return canvas + + def save_image(self, image, filename="fake.jpg"): + image.save(filename) + + def get_random_quote(self): + return random.choice(self.quotes) + + +def fake_handler(message: Message, bot: TeleBot) -> None: + """ignore""" + who = "LiuNeng" + bot.reply_to(message, f"Generating {who}'s fake image") + m = message.text.strip() + prompt = m.strip() + prompt = extract_prompt(message.text, bot.get_me().username) + # Usage + renderer = ImageRenderer() + heros_list = listdir("handlers/heros") + image_path = f"handlers/heros/{random.choice(heros_list)}" + if prompt: + text = prompt + else: + text = renderer.get_random_quote() + rendered_image = renderer.render_image(image_path, text) + renderer.save_image(rendered_image) + with open("fake.jpg", "rb") as f: + bot.send_photo( + message.chat.id, + f, + reply_to_message_id=message.message_id, + caption="Generated image", + ) + + +def fake_photo_handler(message: Message, bot: TeleBot) -> None: + """ignore""" + s = message.caption + s = s.replace("/fake", "").strip() + s = s.replace("fake:", "").strip() + prompt = s.strip() + bot.reply_to(message, f"Generating LiuNeng's fake image") + # get the high quaility picture. + max_size_photo = max(message.photo, key=lambda p: p.file_size) + file_path = bot.get_file(max_size_photo.file_id).file_path + downloaded_file = bot.download_file(file_path) + downloaded_file = bot.download_file(file_path) + with open("fake.jpg", "wb") as temp_file: + temp_file.write(downloaded_file) + renderer = ImageRenderer() + rendered_image = renderer.render_image("fake.jpg", prompt) + renderer.save_image(rendered_image) + with open("fake.jpg", "rb") as f: + bot.send_photo( + message.chat.id, + f, + reply_to_message_id=message.message_id, + caption="Generated image", + ) + + +def register(bot: TeleBot) -> None: + bot.register_message_handler(fake_handler, commands=["fake"], pass_bot=True) + bot.register_message_handler(fake_handler, regexp="^fake:", pass_bot=True) + bot.register_message_handler( + fake_photo_handler, + content_types=["photo"], + func=lambda m: m.caption and m.caption.startswith(("fake:", "/fake")), + pass_bot=True, + )