memebot workflow v1
parent
e5a6248600
commit
2435edc666
|
@ -0,0 +1,117 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from email.policy import default
|
||||||
|
import time
|
||||||
|
import openai
|
||||||
|
import sqlalchemy
|
||||||
|
from sqlalchemy.orm import declarative_base, Session
|
||||||
|
from sqlalchemy import Column, DateTime, String, ForeignKey, Integer, Boolean, Table, and_, or_
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = "user"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key = True)
|
||||||
|
number_of_comments = Column(Integer, default = 0)
|
||||||
|
|
||||||
|
def get_user(user_id : int, session : Session):
|
||||||
|
stmt = sqlalchemy.select(User).where(User.id == user_id)
|
||||||
|
user = session.execute(stmt).scalar_one_or_none()
|
||||||
|
if (user == None):
|
||||||
|
user = User(id = user_id, number_of_comments = 0)
|
||||||
|
session.add(user)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def increase_number_of_comments(user_id : int, session : Session):
|
||||||
|
User.get_user(user_id, session).number_of_comments+=1
|
||||||
|
|
||||||
|
def get_number_of_comments(user_id : int, session : Session) -> int:
|
||||||
|
return User.get_user(user_id, session).number_of_comments
|
||||||
|
|
||||||
|
def reset_number_of_comments(user_id : int, session : Session):
|
||||||
|
User.get_user(user_id, session).number_of_comments = 0
|
||||||
|
|
||||||
|
def reset_all_comments(session : Session):
|
||||||
|
stmt = sqlalchemy.select(User)
|
||||||
|
all_comments = session.execute(stmt).scalars().fetchall()
|
||||||
|
|
||||||
|
for comment in all_comments:
|
||||||
|
comment.number_of_comments = 0
|
||||||
|
|
||||||
|
session.flush()
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
class Comment(Base):
|
||||||
|
__tablename__ = "comment"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key = True)
|
||||||
|
user_comment_id = Column(Integer)
|
||||||
|
bbbb_comment_id = Column(Integer)
|
||||||
|
comment_string = Column(String)
|
||||||
|
|
||||||
|
def get_past_comments(session : Session) -> 'list[str]':
|
||||||
|
stmt = sqlalchemy.select(Comment)
|
||||||
|
return [i.comment_string for i in session.execute(stmt).scalars().fetchall()[0:100]]
|
||||||
|
|
||||||
|
def get_user_comment(user_comment_id:int, session : Session):
|
||||||
|
stmt = sqlalchemy.select(Comment).where(Comment.user_comment_id == user_comment_id)
|
||||||
|
comments = session.execute(stmt).scalars().fetchall()
|
||||||
|
|
||||||
|
if len(comments) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return comments[0]
|
||||||
|
|
||||||
|
def get_bbbb_comment(bbbb_comment_id:int, session : Session):
|
||||||
|
stmt = sqlalchemy.select(Comment).where(Comment.bbbb_comment_id == bbbb_comment_id)
|
||||||
|
comments = session.execute(stmt).scalars().fetchall()
|
||||||
|
|
||||||
|
if len(comments) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return comments[0]
|
||||||
|
|
||||||
|
def get_comment(comment_id : int, session : Session):
|
||||||
|
user_comment = Comment.get_user_comment(comment_id, session)
|
||||||
|
if (user_comment is not None):
|
||||||
|
return user_comment
|
||||||
|
else:
|
||||||
|
return Comment.get_bbbb_comment(comment_id, session)
|
||||||
|
|
||||||
|
def has_replied_to_comment(comment_id : int, session : Session):
|
||||||
|
return Comment.get_comment(comment_id, session) == None
|
||||||
|
|
||||||
|
def create_new_comment(user_comment_id : int, bbbb_comment_id : int, comment_string : str, session : Session):
|
||||||
|
comment = Comment(user_comment_id = user_comment_id, bbbb_comment_id = bbbb_comment_id, comment_string = comment_string)
|
||||||
|
session.add(comment)
|
||||||
|
|
||||||
|
class Post(Base):
|
||||||
|
__tablename__ = "post"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key = True)
|
||||||
|
has_replied = Column(Boolean, default = False)
|
||||||
|
replies_to_post = Column(Integer, default = 0)
|
||||||
|
|
||||||
|
def get_post(post_id : int, session : Session):
|
||||||
|
stmt = sqlalchemy.select(Post).where(Post.id == post_id)
|
||||||
|
post = session.execute(stmt).scalar_one_or_none()
|
||||||
|
if (post == None):
|
||||||
|
post = Post(id = post_id)
|
||||||
|
session.add(post)
|
||||||
|
return post
|
||||||
|
|
||||||
|
def has_replied_to_post(post_id : int, session : Session):
|
||||||
|
return Post.get_post(post_id, session).has_replied
|
||||||
|
|
||||||
|
def increment_replies(post_id : int, session : Session):
|
||||||
|
Post.get_post(post_id, session).replies_to_post += 1
|
||||||
|
|
||||||
|
def get_number_of_replies(post_id : int, session : Session):
|
||||||
|
replies = Post.get_post(post_id, session).replies_to_post
|
||||||
|
if replies == None:
|
||||||
|
replies = 0
|
||||||
|
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def register_post_reply(post_id : int, session : Session):
|
||||||
|
Post.get_post(post_id, session).has_replied = True
|
|
@ -0,0 +1,207 @@
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import meme_generator
|
||||||
|
from RDramaAPIInterface import RDramaAPIInterface
|
||||||
|
from datetime import datetime
|
||||||
|
from os.path import exists, join, realpath, split
|
||||||
|
from threading import Timer
|
||||||
|
from BotModels import Base, Comment, Post, User
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
import os
|
||||||
|
from markdown import markdown
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
TEST_MODE = True
|
||||||
|
TEST_AUTH_TOKEN = "ED3eURMKP9FKBFbi-JUxo8MPGWkEihuyIlAUGtVL7xwx0NEy4Nf6J_mxWYTPgAQx1iy1X91hx7PPHyEBS79hvKVIy5DMEzOyAe9PAc5pmqSJlLGq_-ROewMwFzGrqer4"
|
||||||
|
MINUTES_BEFORE_FORCED_SHUTDOWN = 10
|
||||||
|
DB_FILENAME = "automeme_database.db"
|
||||||
|
PAGES_TO_SCAN = 5
|
||||||
|
AUTOMEME_ID = 3 #TODO
|
||||||
|
ALLOWED_COMMENTS_PER_POST = 20
|
||||||
|
ALLOWED_COMMENTS_PER_USER_PER_DAY = 20
|
||||||
|
|
||||||
|
def get_real_filename(filename : str):
|
||||||
|
path_to_script = realpath(__file__)
|
||||||
|
path_to_script_directory, _ = split(path_to_script)
|
||||||
|
return join(path_to_script_directory, filename)
|
||||||
|
|
||||||
|
# rdrama = RDramaAPIInterface(TEST_AUTH_TOKEN, "localhost", sleep=5, https=False)
|
||||||
|
# print(open('emoji_cache/klanjak.webp', 'rb'))
|
||||||
|
|
||||||
|
# image = meme_generator.create_classic_meme_from_emoji("marseyannoyed", "THAT FACE WHEN YOU", "FORGET TO TAKE OUT THE TRASH")
|
||||||
|
# output = io.BytesIO()
|
||||||
|
# image.save(output, format="webp")
|
||||||
|
# file = {'file': ('based.webp', output.getvalue(), 'image/webp')}
|
||||||
|
# rdrama.reply_to_comment_easy(175, 1, "assddsfssdssdsd", file=file)
|
||||||
|
|
||||||
|
def comment_with_image(image, comment_id, post_id):
|
||||||
|
output = io.BytesIO()
|
||||||
|
image.save(output, format="webp")
|
||||||
|
file = {'file': ('based.webp', output.getvalue(), 'image/webp')}
|
||||||
|
rdrama.reply_to_comment_easy(comment_id, post_id, "ffffff", file=file)
|
||||||
|
|
||||||
|
class TextElement():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Text(TextElement):
|
||||||
|
def __init__(self, text):
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Text({self.text})"
|
||||||
|
|
||||||
|
class Emoji(TextElement):
|
||||||
|
def __init__(self, emoji, big):
|
||||||
|
self.emoji = emoji
|
||||||
|
self.big = big
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Emoji({self.emoji}, big={self.big})"
|
||||||
|
|
||||||
|
def get_text_only(text_elements : list[TextElement]) -> str:
|
||||||
|
text = [i.text for i in text_elements if isinstance(i, Text)]
|
||||||
|
return " ".join(text)
|
||||||
|
|
||||||
|
def text_elements(string : str):
|
||||||
|
EMOJI_REGEX = r"(:[^ ]*:)"
|
||||||
|
elements = re.split(EMOJI_REGEX, string)
|
||||||
|
to_return = []
|
||||||
|
for element in elements:
|
||||||
|
if element == "":
|
||||||
|
continue
|
||||||
|
if not re.match(EMOJI_REGEX, element):
|
||||||
|
to_return.append(Text(element.strip()))
|
||||||
|
else:
|
||||||
|
if "#" in element:
|
||||||
|
big = True
|
||||||
|
element = element.replace("#","")
|
||||||
|
else:
|
||||||
|
big = False
|
||||||
|
element = element.strip(":")
|
||||||
|
to_return.append(Emoji(element, big))
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
def strip_markdown(markdown_string):
|
||||||
|
markdown_string = re.sub(">.*\n", "", markdown_string)
|
||||||
|
try:
|
||||||
|
html = markdown(markdown_string)
|
||||||
|
except AttributeError:
|
||||||
|
html = markdown_string #if there is no markdown in the string you get an error
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
text = ''.join(soup.findAll(text=True))
|
||||||
|
text = re.sub(r"!blackjack[^ ]*", "", text)
|
||||||
|
text = re.sub(r"fortune", "", text)
|
||||||
|
text = re.sub(r"factcheck", "", text)
|
||||||
|
text = re.sub("!slots.*?\s", "", text)
|
||||||
|
text = re.sub(r"\"", "'", text)
|
||||||
|
|
||||||
|
# make sure there are only letters in the string.
|
||||||
|
if len(set(list(text.lower())).intersection(set(["a",'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']))) == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
text = re.sub("@(.*?)\s", "", text)
|
||||||
|
text = re.sub("!slots.*?\s", "", text)
|
||||||
|
|
||||||
|
text = re.sub("(?i)trans lives matter", "", text)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def remove_duplicates(list):
|
||||||
|
return [json.loads(j) for j in set([json.dumps(i) for i in list])]
|
||||||
|
|
||||||
|
def get_eligible_comments(rdrama : RDramaAPIInterface, session : Session):
|
||||||
|
comments = rdrama.get_comments(number_of_pages=PAGES_TO_SCAN)['data']
|
||||||
|
comments = [comment for comment in comments if not comment['is_bot']] #No bots
|
||||||
|
comments = [comment for comment in comments if not comment['author']['id'] == BBBB_ID] #Don't reply to self
|
||||||
|
comments = [comment for comment in comments if Post.get_number_of_replies(comment['post_id'], session) < ALLOWED_COMMENTS_PER_POST] #Don't spam posts
|
||||||
|
comments = [comment for comment in comments if User.get_number_of_comments(comment['author']['id'], session) < ALLOWED_COMMENTS_PER_USER_PER_DAY] #Don't spam users
|
||||||
|
comments = [comment for comment in comments if Comment.get_comment(comment['id'], session) is None] #Double check that we haven't replied to the comment
|
||||||
|
comments = remove_duplicates(comments) #Remove the duplicates
|
||||||
|
return comments
|
||||||
|
|
||||||
|
def main_processing_task(rdrama : RDramaAPIInterface, session : Session):
|
||||||
|
is_chudded = False #Do we have the chud award?
|
||||||
|
can_communicate = True #Can we send any message at all?
|
||||||
|
is_pizzad = False
|
||||||
|
rdrama.get_front_page()
|
||||||
|
bbbb_information = rdrama.get_user_information(BBBB_ID)
|
||||||
|
print(f"coins: {bbbb_information['coins']} comments: {bbbb_information['comment_count']}")
|
||||||
|
for badge in bbbb_information['badges']:
|
||||||
|
if (badge['name'] == "Marsey Award"):
|
||||||
|
print("We have the marsey award. STOP.")
|
||||||
|
can_communicate = False
|
||||||
|
if (badge['name'] == "Chud"):
|
||||||
|
print("We have the CHUD award. CONTINUE")
|
||||||
|
is_chudded = True
|
||||||
|
if (badge['name'] == "Bird Site Award"):
|
||||||
|
print("We have the Bird Site Award. STOP.")
|
||||||
|
can_communicate = False
|
||||||
|
if (badge['name'] == "Pizzashill Award"):
|
||||||
|
print("We have the Pizzashill Award. CONTINUE.")
|
||||||
|
is_pizzad = True
|
||||||
|
|
||||||
|
if bbbb_information['is_banned']:
|
||||||
|
print("We are banned. STOP.")
|
||||||
|
can_communicate = False
|
||||||
|
|
||||||
|
if can_communicate:
|
||||||
|
eligible_comments = get_eligible_comments(rdrama, session)
|
||||||
|
for eligible_comment in eligible_comments:
|
||||||
|
comment_text = eligible_comment['body']
|
||||||
|
cleaned_comment_text = strip_markdown(comment_text)
|
||||||
|
|
||||||
|
comment_lines = cleaned_comment_text.split("\n")
|
||||||
|
comment_lines = [comment_line for comment_line in comment_lines if comment_line != ""]
|
||||||
|
element_lines = [text_elements(line) for line in comment_lines]
|
||||||
|
|
||||||
|
image = None
|
||||||
|
if (len(element_lines) == 2):
|
||||||
|
if isinstance(element_lines[0][0], Emoji) and isinstance(element_lines[1][0], Emoji):
|
||||||
|
emoji1 = element_lines[0][0].emoji
|
||||||
|
emoji2 = element_lines[1][0].emoji
|
||||||
|
caption1 = get_text_only(element_lines[0][1:])
|
||||||
|
caption2 = get_text_only(element_lines[1][1:])
|
||||||
|
|
||||||
|
image = meme_generator.create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2)
|
||||||
|
|
||||||
|
if image != None:
|
||||||
|
comment_with_image(image, eligible_comment['id'], eligible_comment['post_id'])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
TEST_AUTH_TOKEN = "ED3eURMKP9FKBFbi-JUxo8MPGWkEihuyIlAUGtVL7xwx0NEy4Nf6J_mxWYTPgAQx1iy1X91hx7PPHyEBS79hvKVIy5DMEzOyAe9PAc5pmqSJlLGq_-ROewMwFzGrqer4"
|
||||||
|
print(f"======= RUNNING AT {datetime.now().hour}:{datetime.now().minute} ======= ")
|
||||||
|
if TEST_MODE:
|
||||||
|
website = "localhost"
|
||||||
|
auth = TEST_AUTH_TOKEN
|
||||||
|
https = False
|
||||||
|
timeout = 1
|
||||||
|
BBBB_ID = 6
|
||||||
|
OPERATOR_ID = 9
|
||||||
|
ACTUALLY_CALL_OPEN_AI = False
|
||||||
|
else:
|
||||||
|
website = "rdrama.net"
|
||||||
|
with open(get_real_filename("rdrama_auth_token"), "r") as f:
|
||||||
|
auth = f.read()
|
||||||
|
https = True
|
||||||
|
timeout = 10
|
||||||
|
rdrama = RDramaAPIInterface(auth, website, timeout, https=https)
|
||||||
|
|
||||||
|
#Set up fail safe
|
||||||
|
def exitfunc():
|
||||||
|
print("*That's it, you're going in the retard squisher.*")
|
||||||
|
print("FAILSAFE FORCED SHUTDOWN", datetime.now())
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
timer = Timer(60*MINUTES_BEFORE_FORCED_SHUTDOWN, exitfunc)
|
||||||
|
timer.start()
|
||||||
|
engine = create_engine(f"sqlite:///{get_real_filename(DB_FILENAME)}")
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
main_processing_task(rdrama, session)
|
||||||
|
session.commit()
|
||||||
|
timer.cancel()
|
|
@ -0,0 +1,366 @@
|
||||||
|
import math
|
||||||
|
from tkinter import UNITS
|
||||||
|
from typing import Union
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageDraw
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageOps
|
||||||
|
import requests
|
||||||
|
import io
|
||||||
|
from image_utils import ImageText
|
||||||
|
from os.path import exists
|
||||||
|
|
||||||
|
HIGHLIGHT_MODE = False
|
||||||
|
|
||||||
|
class ColorScheme:
|
||||||
|
BLACK = 0
|
||||||
|
WHITE_WITH_BLACK_BORDER = 1
|
||||||
|
|
||||||
|
def create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2):
|
||||||
|
LEFT_MARGIN_COLUMN = 20
|
||||||
|
CONTENT_COLUMN = 300
|
||||||
|
MIDDLE_MARGIN_COLUMN = 80
|
||||||
|
RIGHT_MARGIN_COLUMN = LEFT_MARGIN_COLUMN
|
||||||
|
TOP_MARGIN_ROW = 80
|
||||||
|
IMAGE_ROW = 300
|
||||||
|
MIDDLE_MARGIN_ROW = 20
|
||||||
|
TEXT_ROW = 100
|
||||||
|
BOTTOM_MARGIN_ROW = 200
|
||||||
|
|
||||||
|
total_image_size_x = 2*CONTENT_COLUMN + LEFT_MARGIN_COLUMN + RIGHT_MARGIN_COLUMN + MIDDLE_MARGIN_COLUMN
|
||||||
|
total_image_size_y = TOP_MARGIN_ROW + IMAGE_ROW + MIDDLE_MARGIN_ROW + TEXT_ROW + BOTTOM_MARGIN_ROW
|
||||||
|
|
||||||
|
left_image = get_emoji_from_rdrama(emoji1)
|
||||||
|
right_image = get_emoji_from_rdrama(emoji2)
|
||||||
|
|
||||||
|
#Resize images
|
||||||
|
left_image = ImageOps.contain(left_image, (CONTENT_COLUMN, IMAGE_ROW))
|
||||||
|
right_image = ImageOps.contain(right_image, (CONTENT_COLUMN, IMAGE_ROW))
|
||||||
|
|
||||||
|
right_image = ImageOps.mirror(right_image)
|
||||||
|
|
||||||
|
#Base image
|
||||||
|
base = Image.new(mode="RGB", size=(total_image_size_x,total_image_size_y), color=(255,255,255))
|
||||||
|
|
||||||
|
#Add images
|
||||||
|
center_and_paste(base, left_image, (LEFT_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
||||||
|
center_and_paste(base, right_image, (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
||||||
|
|
||||||
|
#Text regions
|
||||||
|
add_text_box(base,
|
||||||
|
caption1,
|
||||||
|
(CONTENT_COLUMN, TEXT_ROW),
|
||||||
|
(LEFT_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW),
|
||||||
|
init_font_size=50,
|
||||||
|
align="cht",
|
||||||
|
font="impact.ttf",
|
||||||
|
color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
||||||
|
add_text_box(base,
|
||||||
|
caption2,
|
||||||
|
(CONTENT_COLUMN, TEXT_ROW),
|
||||||
|
(LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW),
|
||||||
|
init_font_size=50,
|
||||||
|
align="cht",
|
||||||
|
font="impact.ttf",
|
||||||
|
color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
||||||
|
return add_watermark(base)
|
||||||
|
|
||||||
|
def create_classic_meme(image: Image, top_caption : str, bottom_caption : str):
|
||||||
|
image_x_size, image_y_size = image.size
|
||||||
|
UNIT = 5
|
||||||
|
caption_size = int(image_y_size/UNIT)
|
||||||
|
|
||||||
|
add_text_box(image, top_caption, (image_x_size, caption_size), (0,0), "impact.ttf", 60, align="cvch", color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
||||||
|
add_text_box(image, bottom_caption, (image_x_size, caption_size), (0,int((UNIT-1)*(image_y_size/UNIT))), "impact.ttf", 60, align="cvch", color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
def create_classic_meme_from_url(url : str, top_caption : str, bottom_caption : str):
|
||||||
|
return create_classic_meme(get_image_file_from_url(url), top_caption, bottom_caption)
|
||||||
|
|
||||||
|
def create_classic_meme_from_emoji(emoji : str, top_caption : str, bottom_caption : str):
|
||||||
|
EMOJI_SIZE = 400
|
||||||
|
BORDER_SIZE = 100
|
||||||
|
|
||||||
|
marsey = get_emoji_from_rdrama(emoji)
|
||||||
|
marsey = ImageOps.contain(marsey, (EMOJI_SIZE, EMOJI_SIZE))
|
||||||
|
|
||||||
|
base = Image.new(mode="RGB", size=(EMOJI_SIZE+2*BORDER_SIZE, EMOJI_SIZE+2*BORDER_SIZE), color=(255,255,255))
|
||||||
|
base.paste(marsey, (BORDER_SIZE, BORDER_SIZE), marsey)
|
||||||
|
|
||||||
|
return create_classic_meme(base, top_caption, bottom_caption)
|
||||||
|
|
||||||
|
def create_modern_meme(image : Image, caption : str):
|
||||||
|
CAPTION_SIZE = 150
|
||||||
|
image_x_size, image_y_size = image.size
|
||||||
|
base = Image.new(mode="RGB", size=(image_x_size,image_y_size+CAPTION_SIZE), color=(255,255,255))
|
||||||
|
base.paste(image, (0, CAPTION_SIZE))
|
||||||
|
add_text_box(base, caption, (image_x_size, CAPTION_SIZE), (0,0), font="intreb.ttf",align="chcv")
|
||||||
|
return base
|
||||||
|
|
||||||
|
def create_modern_meme_from_url(url : str, caption : str):
|
||||||
|
return create_modern_meme(get_image_file_from_url(url), caption)
|
||||||
|
|
||||||
|
def create_modern_meme_from_emoji(emoji: str, caption: str):
|
||||||
|
EMOJI_SIZE = 400
|
||||||
|
BORDER_SIZE = 10
|
||||||
|
|
||||||
|
marsey = get_emoji_from_rdrama(emoji)
|
||||||
|
marsey = ImageOps.contain(marsey, (EMOJI_SIZE, EMOJI_SIZE))
|
||||||
|
|
||||||
|
base = Image.new(mode="RGB", size=(EMOJI_SIZE+2*BORDER_SIZE, EMOJI_SIZE+2*BORDER_SIZE), color=(255,255,255))
|
||||||
|
base.paste(marsey, (BORDER_SIZE, BORDER_SIZE), marsey)
|
||||||
|
|
||||||
|
return create_modern_meme(base, caption)
|
||||||
|
|
||||||
|
class WebcomicPanel():
|
||||||
|
PANEL_SIZE = 400
|
||||||
|
FONT = "Little Story.ttf"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_image(self) -> Image:
|
||||||
|
return Image.new(mode="RGB", size=(self.PANEL_SIZE,self.PANEL_SIZE), color=(255,255,255))
|
||||||
|
|
||||||
|
class OneCharacterWebcomicPanel(WebcomicPanel):
|
||||||
|
def __init__(self, emoji, caption, words_in_background):
|
||||||
|
self.emoji = emoji
|
||||||
|
self.caption = caption
|
||||||
|
self.words_in_background = words_in_background
|
||||||
|
|
||||||
|
def create_image(self) -> Image:
|
||||||
|
base = super().create_image()
|
||||||
|
panel_size_x, panel_size_y = base.size
|
||||||
|
|
||||||
|
# We put the text in the background of the panel.
|
||||||
|
text_region_x_size = int(panel_size_x)
|
||||||
|
text_region_y_size = int(panel_size_y if self.words_in_background else panel_size_y/2)
|
||||||
|
add_text(base, self.caption, (text_region_x_size, text_region_y_size), (0,0), font=super().FONT)
|
||||||
|
|
||||||
|
# We put marsey in the bottom left quadrant
|
||||||
|
emoji_region_x_size = int(panel_size_x)
|
||||||
|
emoji_region_y_size = int(panel_size_y/2)
|
||||||
|
emoji_placement_position_x = 0
|
||||||
|
emoji_placement_position_y = int(panel_size_y/2)
|
||||||
|
emoji = get_emoji_from_rdrama(self.emoji)
|
||||||
|
center_and_paste(base, emoji, (emoji_placement_position_x, emoji_placement_position_y), (emoji_region_x_size, emoji_region_y_size))
|
||||||
|
|
||||||
|
return add_border_to_image(base)
|
||||||
|
|
||||||
|
class TwoCharacterWebcomicPanel(WebcomicPanel):
|
||||||
|
def __init__(self, left_emoji, left_caption, right_emoji, right_caption):
|
||||||
|
self.left_emoji = left_emoji
|
||||||
|
self.left_caption = left_caption
|
||||||
|
self.right_emoji = right_emoji
|
||||||
|
self.right_caption = right_caption
|
||||||
|
|
||||||
|
def create_image(self) -> Image:
|
||||||
|
base = super().create_image()
|
||||||
|
panel_size_x, panel_size_y = base.size
|
||||||
|
|
||||||
|
# We put the left marsey in the bottom left quadrant
|
||||||
|
left_emoji_region_x_size = int(panel_size_x/2)
|
||||||
|
left_emoji_region_y_size = int(panel_size_y/2)
|
||||||
|
left_emoji_placement_position_x = 0
|
||||||
|
left_emoji_placement_position_y = int(panel_size_y/2)
|
||||||
|
left_emoji = get_emoji_from_rdrama(self.left_emoji)
|
||||||
|
center_and_paste(base, left_emoji, (left_emoji_placement_position_x, left_emoji_placement_position_y), (left_emoji_region_x_size, left_emoji_region_y_size))
|
||||||
|
|
||||||
|
# We put the right marsey in the bottom right quadrant
|
||||||
|
right_emoji_region_x_size = int(panel_size_x/2)
|
||||||
|
right_emoji_region_y_size = int(panel_size_y/2)
|
||||||
|
right_emoji_placement_position_x = int(panel_size_x/2)
|
||||||
|
right_emoji_placement_position_y = int(panel_size_y/2)
|
||||||
|
right_emoji = get_emoji_from_rdrama(self.right_emoji)
|
||||||
|
center_and_paste(base, right_emoji, (right_emoji_placement_position_x, right_emoji_placement_position_y), (right_emoji_region_x_size, right_emoji_region_y_size))
|
||||||
|
|
||||||
|
#Each text caption will get 5/8 of the width, and 1/4 of the height of the panel.
|
||||||
|
#The left caption will be all the way to the left. The right caption will be 3/8 to the right.
|
||||||
|
|
||||||
|
CAPTION_UNITS = 5
|
||||||
|
CAPTION_DIVISOR = 8
|
||||||
|
|
||||||
|
# We put the text in the top half of the panel.
|
||||||
|
left_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR))
|
||||||
|
left_text_region_y_size = int(panel_size_y/4)
|
||||||
|
left_text_region_x_position = 0
|
||||||
|
left_text_region_y_position = 0
|
||||||
|
add_text_box(base, self.left_caption, (left_text_region_x_size, left_text_region_y_size), (left_text_region_x_position,left_text_region_y_position), font=super().FONT, align="bl")
|
||||||
|
|
||||||
|
# We put the text in the top half of the panel.
|
||||||
|
right_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR))
|
||||||
|
right_text_region_y_size = int(panel_size_y/4)
|
||||||
|
right_text_region_x_position = int((CAPTION_DIVISOR-CAPTION_UNITS)*(panel_size_x/8))
|
||||||
|
right_text_region_y_position = int(panel_size_y/4)
|
||||||
|
add_text_box(base, self.right_caption, (right_text_region_x_size, right_text_region_y_size), (right_text_region_x_position,right_text_region_y_position), font=super().FONT, align="br")
|
||||||
|
|
||||||
|
return add_border_to_image(base)
|
||||||
|
|
||||||
|
class TitleCardWebcomicPanel(WebcomicPanel):
|
||||||
|
def __init__(self, caption):
|
||||||
|
self.caption = caption
|
||||||
|
|
||||||
|
def create_image(self) -> Image:
|
||||||
|
base = super().create_image()
|
||||||
|
|
||||||
|
add_text_box(base, self.caption, base.size, (0,0), font=super().FONT, init_font_size=90, align="cvch")
|
||||||
|
|
||||||
|
return add_border_to_image(base)
|
||||||
|
|
||||||
|
def create_webcomic(layout : 'list[WebcomicPanel]'):
|
||||||
|
assumed_panel_x_size, assumed_panel_y_size = layout[0].create_image().size
|
||||||
|
total_image_x_size = assumed_panel_x_size * 2
|
||||||
|
total_image_y_size = assumed_panel_x_size * math.ceil(len(layout)/2)
|
||||||
|
|
||||||
|
image = Image.new(mode="RGB", size=(total_image_x_size, total_image_y_size), color=(255,255,255))
|
||||||
|
for i in range(len(layout)):
|
||||||
|
panel = layout[i]
|
||||||
|
x = i%2
|
||||||
|
y = math.floor(i/2)
|
||||||
|
image.paste(panel.create_image(), (x*assumed_panel_x_size, y*assumed_panel_y_size))
|
||||||
|
return add_watermark(image)
|
||||||
|
|
||||||
|
def add_text_box(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf", init_font_size = 45, align :str = "", color = ColorScheme.BLACK):
|
||||||
|
if caption == "":
|
||||||
|
return
|
||||||
|
if init_font_size == 0:
|
||||||
|
print("Retard moment")
|
||||||
|
return
|
||||||
|
region_x_size, region_y_size = region_size
|
||||||
|
line_image = ImageText((region_x_size, region_y_size))
|
||||||
|
if color == ColorScheme.BLACK:
|
||||||
|
fill_color = (0,0,0)
|
||||||
|
stroke = None
|
||||||
|
stroke_size = 0
|
||||||
|
if color == ColorScheme.WHITE_WITH_BLACK_BORDER:
|
||||||
|
fill_color = (255,255,255)
|
||||||
|
stroke = (0,0,0)
|
||||||
|
stroke_size = 3
|
||||||
|
|
||||||
|
place = "left"
|
||||||
|
if "ch" in align:
|
||||||
|
place = "center"
|
||||||
|
|
||||||
|
actual_text_box_size = line_image.write_text_box((0,0), caption, region_x_size, font_size=init_font_size, font_filename=font, color=fill_color, stroke_color=stroke, stroke_size=stroke_size, place=place)
|
||||||
|
_, actual_text_box_y_size, input_text_block_x_size, actual_text_box_x_size = actual_text_box_size
|
||||||
|
if actual_text_box_y_size <= region_y_size:
|
||||||
|
actual_paste_x_coordinates, actual_paste_y_coordinates = coordinates
|
||||||
|
if align != "":
|
||||||
|
y_difference = region_y_size - actual_text_box_y_size
|
||||||
|
x_difference = region_x_size - actual_text_box_x_size
|
||||||
|
# Horizontal Align
|
||||||
|
if "t" in align:
|
||||||
|
#This is the default
|
||||||
|
pass
|
||||||
|
elif "b" in align:
|
||||||
|
actual_paste_y_coordinates+=y_difference
|
||||||
|
elif "cv" in align:
|
||||||
|
actual_paste_y_coordinates += int(y_difference/2)
|
||||||
|
|
||||||
|
if "l" in align:
|
||||||
|
#This is the default
|
||||||
|
pass
|
||||||
|
elif "r" in align:
|
||||||
|
|
||||||
|
actual_paste_x_coordinates+=x_difference
|
||||||
|
# elif "ch" in align:
|
||||||
|
# actual_paste_x_coordinates += int(x_difference/2)
|
||||||
|
|
||||||
|
if HIGHLIGHT_MODE:
|
||||||
|
image_draw = ImageDraw.Draw(base)
|
||||||
|
image_draw.rectangle((coordinates, (coordinates[0]+region_size[0], coordinates[1]+region_size[1])), fill="blue")
|
||||||
|
image_draw.rectangle(((actual_paste_x_coordinates, actual_paste_y_coordinates), (actual_paste_x_coordinates+actual_text_box_x_size, actual_text_box_y_size+actual_paste_y_coordinates)), fill="tan")
|
||||||
|
|
||||||
|
base.paste(line_image.image, (actual_paste_x_coordinates, actual_paste_y_coordinates), line_image.image)
|
||||||
|
else:
|
||||||
|
add_text_box(base, caption, region_size, coordinates, font=font, init_font_size=init_font_size-1, align=align, color=color)
|
||||||
|
|
||||||
|
def add_text(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf"):
|
||||||
|
if caption == "":
|
||||||
|
return
|
||||||
|
region_x_size, region_y_size = region_size
|
||||||
|
line_image = ImageText((region_x_size, region_y_size))
|
||||||
|
line_image.fill_text_box((0,0), caption, region_x_size, region_y_size, font_filename=font)
|
||||||
|
base.paste(line_image.image, coordinates, line_image.image)
|
||||||
|
|
||||||
|
def add_watermark(image : Image):
|
||||||
|
WATERMARK_HEIGHT = 30
|
||||||
|
image_size_x, image_size_y = image.size
|
||||||
|
base = Image.new(mode="RGB", size=(image_size_x, image_size_y + WATERMARK_HEIGHT), color=(255,255,255))
|
||||||
|
base.paste(image)
|
||||||
|
|
||||||
|
marsey = get_emoji_from_rdrama("marseyjamming")
|
||||||
|
marsey = ImageOps.contain(marsey, (WATERMARK_HEIGHT, WATERMARK_HEIGHT))
|
||||||
|
base.paste(marsey, (0, image_size_y), marsey)
|
||||||
|
|
||||||
|
text_line_size = int(WATERMARK_HEIGHT/2)
|
||||||
|
|
||||||
|
add_text(base, "A meme by HeyMoon and Foo", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y))
|
||||||
|
add_text(base, "For instructions on how to legally build a pipe bomb, go to rdrama.net", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y+text_line_size))
|
||||||
|
|
||||||
|
return base
|
||||||
|
|
||||||
|
def center_and_paste(base : Image, to_paste : Image, coordinates: tuple[int, int], box_size : tuple[int, int]):
|
||||||
|
to_paste = ImageOps.contain(to_paste, box_size)
|
||||||
|
|
||||||
|
image_size_x, image_size_y = to_paste.size
|
||||||
|
box_size_x, box_size_y = box_size
|
||||||
|
|
||||||
|
extra_space_x = box_size_x - image_size_x
|
||||||
|
extra_space_y = box_size_y - image_size_y
|
||||||
|
|
||||||
|
offset_x = int(extra_space_x/2)
|
||||||
|
offset_y = int(extra_space_y/2)
|
||||||
|
|
||||||
|
box_coordinate_x, box_coordinate_y = coordinates
|
||||||
|
|
||||||
|
x, y = offset_x + box_coordinate_x, offset_y + box_coordinate_y
|
||||||
|
|
||||||
|
base.paste(to_paste, (x, y), to_paste)
|
||||||
|
|
||||||
|
def add_border_to_image(image : Image, thickness : int = 5):
|
||||||
|
inner_image_x_size, inner_image_y_size = image.size
|
||||||
|
outer_image_x_size, outer_image_y_size = inner_image_x_size + 2*thickness, inner_image_y_size + 2*thickness
|
||||||
|
outside_image = Image.new(mode="RGB", size=(outer_image_x_size,outer_image_y_size), color=(0,0,0))
|
||||||
|
|
||||||
|
outside_image.paste(image, (thickness, thickness))
|
||||||
|
return outside_image
|
||||||
|
|
||||||
|
def get_emoji_from_rdrama(emoji_name):
|
||||||
|
cleaned_emoji_name : str = emoji_name
|
||||||
|
should_flip = False
|
||||||
|
if '!' in emoji_name:
|
||||||
|
cleaned_emoji_name = cleaned_emoji_name.replace("!", "")
|
||||||
|
should_flip = True
|
||||||
|
|
||||||
|
if (exists(f"emoji_cache/{cleaned_emoji_name}.webp")):
|
||||||
|
image = Image.open(f"emoji_cache/{cleaned_emoji_name}.webp")
|
||||||
|
else:
|
||||||
|
image = get_image_file_from_url(f"https://www.rdrama.net/e/{cleaned_emoji_name}.webp")
|
||||||
|
image.save(f"emoji_cache/{cleaned_emoji_name}.webp")
|
||||||
|
if should_flip:
|
||||||
|
image = ImageOps.mirror(image)
|
||||||
|
return image
|
||||||
|
|
||||||
|
def get_image_file_from_url(url):
|
||||||
|
r = requests.get(url)
|
||||||
|
image_file = io.BytesIO(r.content)
|
||||||
|
im = Image.open(image_file)
|
||||||
|
return im
|
||||||
|
|
||||||
|
#create_soy_vs_chad_meme("bigsmilesoyjak", "!marseyshooting", "I have fun new toys and games for your children", "Die").show()
|
||||||
|
|
||||||
|
# create_webcomic([
|
||||||
|
# TwoCharacterWebcomicPanel("soyjak", "Black people deserve the rope", "marseyconfused", "Why?"),
|
||||||
|
# TwoCharacterWebcomicPanel("seethejak", "Because they are degenerate!!!", "marseysmug", "Kinda like you?"),
|
||||||
|
# OneCharacterWebcomicPanel("soycry", "No!! I am a pure aryan white boy and I am special Hitler told me so, so fuck off you nigger cunt. God I hate you so much, if I commit suicide its gonna be your fault, fuck you, its not fair, why do you always have to bully me around, you are such a bitch, i hate you so much, hitler was right and six million wasnt nearly enough, the jews are responsible for this and they will be punished on the day of the rope, youll be sorry then, plus i am armed and dangerous and i have a gun and i will shoot you if you keep making fun of me plus youll be in troulbe if I kill myself so how do you like that you stupid nigger bitch. fuck", words_in_background=True),
|
||||||
|
# TitleCardWebcomicPanel("One Hour Later..."),
|
||||||
|
# TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"),
|
||||||
|
# OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False)
|
||||||
|
# ]).show()
|
||||||
|
|
||||||
|
#create_classic_meme_from_url("https://rdrama.net/images/16603133970578706.webp", "in all seriousness it probably isn't worth the effort because this is just one of many ways they could goad the bot into saying something innapropriate. Like who cares lol its just one of many things they could say.", "also there isn't really any specific harm done to anyone by a bot saying pedophile nonsense, it's offensive at worst and funny at best. i laughed at least. also also this legit is something that only happens every 2500 comments or so (there was another comment a while back where bbbb said something similar)").show()
|
||||||
|
#create_modern_meme_from_url("https://cdn.discordapp.com/attachments/1007776259910152292/1007833711540174885/unknown.png", "and then heymoon says the imposter is among us").show()
|
||||||
|
#create_classic_meme_from_emoji("marseycock", "I WANT TO PUT MY DICK", "INSIDE OF MARSEY").show()
|
||||||
|
#create_modern_meme_from_emoji("rdramajanny", "youll be sorry when i get my mop you BITCH").show()
|
Loading…
Reference in New Issue