master
HeyMoon 2022-10-19 19:42:57 -05:00
parent 3ba8fb6d50
commit 5362f8374c
3 changed files with 359 additions and 194 deletions

View File

@ -4,7 +4,7 @@ import re
import traceback import traceback
from typing import Callable, TypeVar from typing import Callable, TypeVar
import meme_generator import meme_generator
from meme_generator import WebcomicPanel, OneCharacterWebcomicPanel, TwoCharacterWebcomicPanel, TitleCardWebcomicPanel, add_watermark, create_webcomic from meme_generator import AnimatedImage, WebcomicPanel, OneCharacterWebcomicPanel, TwoCharacterWebcomicPanel, TitleCardWebcomicPanel, add_watermark, create_webcomic
from RDramaAPIInterface import RDramaAPIInterface from RDramaAPIInterface import RDramaAPIInterface
from datetime import datetime, timedelta from datetime import datetime, timedelta
from os.path import exists, join, realpath, split from os.path import exists, join, realpath, split
@ -16,6 +16,7 @@ import os
from markdown import markdown from markdown import markdown
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from utils import get_real_filename from utils import get_real_filename
from pympler import tracker, asizeof, classtracker
ANTISPAM_MESSAGES = [ ANTISPAM_MESSAGES = [
'i have a meme for u :marseyshy:', 'i have a meme for u :marseyshy:',
@ -45,20 +46,20 @@ ANTISPAM_MESSAGES = [
TEST_MODE = False TEST_MODE = False
DRY_MODE = False DRY_MODE = False
BETA_MODE = False BETA_MODE = False
TEST_AUTH_TOKEN = "lawoSNzuNeBRJBld0boApOceCNSEqBhiRu0aoUWh9kTK7AV37NECZoAK-mEUJ1PM1SsTfTY4f3t_LjooMq4QgPLqPC-F7LNGHQ6_0RFacZmIvC2ixOHPKM821RroJexn" TEST_AUTH_TOKEN = "2PR7FAqx0UVM71HayM33p1OTy_zFGOK1rJlCNi1qzvFJqQGK6NOm7I-mSfYjZgs02FU_LqLTPqDa8KN-LUiGSJ1idFfE5jKnG3DUk9rQjn_ZrAtG_VU9GK5F_EGmixg1"
MINUTES_BEFORE_FORCED_SHUTDOWN = 10 MINUTES_BEFORE_FORCED_SHUTDOWN = 10
DB_FILENAME = "automeme_database.db" DB_FILENAME = "automeme_database.db"
PAGES_TO_SCAN = 5 MAX_PAGES_TO_SCAN = 10
AUTOMEME_ID = 13427 AUTOMEME_ID = 13427
ALLOWED_COMMENTS_PER_POST = 20 ALLOWED_COMMENTS_PER_POST = 20
ALLOWED_COMMENTS_PER_USER_PER_DAY = 20 ALLOWED_COMMENTS_PER_USER_PER_DAY = 20
SOY_VS_CHAD_TRIGGER_CHANGE = 1.0 SOY_VS_CHAD_TRIGGER_CHANGE = 0.5
MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.01 MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.01
MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.05 MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.05
CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.02 CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.02
CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.1 CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.1
WEBCOMIC_TRIGGER_CHANCE = 1.0 WEBCOMIC_TRIGGER_CHANCE = 0.5
FREE_POSTS = [6, 97416, 98286] FREE_POSTS = [6, 97416, 98286]
RESTRICTED_POSTS = [5, 16583, 75878, 35835] RESTRICTED_POSTS = [5, 16583, 75878, 35835]
@ -111,7 +112,7 @@ class TextLine:
@property @property
def is_argument_line(self): def is_argument_line(self):
return len(self.emojis) == 2 and (len(self.captions) == 2 or len(self.captions) == 1) and isinstance(self.line[0], Emoji) and isinstance(self.line[2], Emoji) return len(self.emojis) == 2 and ((len(self.captions) == 0) or (len(self.captions) == 2 or len(self.captions) == 1) and isinstance(self.line[0], Emoji) and isinstance(self.line[2], Emoji))
@property @property
def is_pure_text_line(self): def is_pure_text_line(self):
@ -125,6 +126,12 @@ class TextLine:
def is_image_line(self): def is_image_line(self):
return len(self.images) == 1 and len(self.captions) == 0 return len(self.images) == 1 and len(self.captions) == 0
def __str__(self):
return str(self.line)
def __repr__(self) -> str:
return str(self.line)
class TextElement(): class TextElement():
pass pass
@ -211,9 +218,10 @@ def strip_markdown(markdown_string):
text = re.sub("@(.*?)\s", "", text) text = re.sub("@(.*?)\s", "", text)
text = re.sub("!slots.*?\s", "", text) text = re.sub("!slots.*?\s", "", text)
text = re.sub("(?i)detrans lives matter", "", text)
text = re.sub("(?i)trans lives matter", "", text) text = re.sub("(?i)trans lives matter", "", text)
return text return text.strip()
def remove_duplicates(list): def remove_duplicates(list):
return [json.loads(j) for j in set([json.dumps(i) for i in list])] return [json.loads(j) for j in set([json.dumps(i) for i in list])]
@ -254,7 +262,8 @@ def get_eligible_comments(rdrama : RDramaAPIInterface, session : Session):
comments = remove_duplicates(comments) #Remove the duplicates comments = remove_duplicates(comments) #Remove the duplicates
print([comment['id'] for comment in comments]) print([comment['id'] for comment in comments])
ScriptCall.register(session)
session.commit()
return comments return comments
T = TypeVar('T') T = TypeVar('T')
@ -272,14 +281,15 @@ def extract_directives(string : str):
list = [i.lower()[2:] for i in list] list = [i.lower()[2:] for i in list]
return list return list
def create_comment_message(chud: bool, pizza: bool, bird : bool, marsey : bool): def create_comment_message(chud: bool, pizza: bool, bird : bool, marsey : bool, mention : str = None):
if (marsey): if (marsey):
return ":marseyshy:" return ":marseyshy:"
message = choice(ANTISPAM_MESSAGES) +"\n" message = choice(ANTISPAM_MESSAGES) +"\n"
if (mention != None):
message += f"@{mention}\n"
message += f"<source src=\"{''.join(choices(['a','b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','z'], k=500))}.mp3\">" message += f"<source src=\"{''.join(choices(['a','b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','z'], k=500))}.mp3\">"
if (chud): if (chud):
@ -289,6 +299,176 @@ def create_comment_message(chud: bool, pizza: bool, bird : bool, marsey : bool):
return message return message
def handle_comment(comment : dict, rdrama : RDramaAPIInterface, session : Session, chud: bool, pizza: bool, bird : bool, is_marsey : bool, starting_directives : 'list[str]'= [], mention : str = None):
try:
begin = datetime.now()
under_post_limit = Post.get_number_of_replies(comment['post_id'], session) < ALLOWED_COMMENTS_PER_POST
under_user_limit = User.get_number_of_comments(0 if comment['author'] == '👻' else comment['author']['id'], session) < ALLOWED_COMMENTS_PER_USER_PER_DAY
has_not_replied_to_comment = Comment.get_comment(comment['id'], session) is None
is_not_restricted_post = comment['post_id'] not in RESTRICTED_POSTS
if (not (under_post_limit and under_user_limit and (has_not_replied_to_comment or len(starting_directives) != 0) and is_not_restricted_post)):
return
comment_text = comment['body']
directives = extract_directives(comment_text) if len(starting_directives) == 0 else starting_directives
cleaned_comment_text = strip_markdown(comment_text)
if (cleaned_comment_text == "" and len(directives) > 0):
parent_id = comment['parent_comment_id']
username = None if comment['author'] == '👻' else comment['author']['username']
parent = rdrama.get_comment(parent_id)
handle_comment(parent, rdrama, session, chud, pizza, bird, is_marsey, starting_directives=directives, mention=username)
return
if ("meme" in directives or comment['post_id'] in FREE_POSTS):
random_float = 0.0
else:
random_float = random()
comment_lines = cleaned_comment_text.split("\n")
comment_lines = [comment_line for comment_line in comment_lines if comment_line != ""]
element_lines = [TextLine(line) for line in comment_lines]
argument_lines_count, dialog_lines_count, text_lines_count, big_marsey_lines_count = 0,0,0,0
dialog_lines = list(filter(lambda a : a.is_dialogue_line, element_lines))
argument_lines = list(filter(lambda a : a.is_argument_line, element_lines))
pure_text_lines = list(filter(lambda a : a.is_pure_text_line, element_lines))
big_marsey_lines = list(filter(lambda a : a.is_big_marsey_line, element_lines))
image_lines = list(filter(lambda a : a.is_image_line, element_lines))
argument_lines_count = len(argument_lines)
dialog_lines_count = len(dialog_lines)
pure_text_lines_count = len(pure_text_lines)
big_marsey_lines_count = len(big_marsey_lines)
image_lines_count = len(image_lines)
image = None
if (dialog_lines_count == 2):
print(f"[{comment['id']}] SOY_VS_CHAD")
if (random_float <= SOY_VS_CHAD_TRIGGER_CHANGE):
#Soy vs Chad
line1 = dialog_lines[0]
line2 = dialog_lines[1]
emoji1 = line1.emojis[0].emoji
emoji2 = line2.emojis[0].emoji
caption1 = line1.text
caption2 = line2.text
image = meme_generator.create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2)
elif (big_marsey_lines_count == 1 and pure_text_lines_count == 1):
print(f"[{comment['id']}] MODERN_MEME_WITH_MARSEY")
if (random_float <= MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE):
# Modern Meme with Marsey
text_line = pure_text_lines[0]
marsey_line = big_marsey_lines[0]
marsey = marsey_line.emojis[0].emoji
caption = text_line.text
image = meme_generator.create_modern_meme_from_emoji(marsey, caption)
elif (image_lines_count == 1 and pure_text_lines_count == 1):
print(f"[{comment['id']}] MODERN_MEME_WITH_IMAGE")
if (random_float <= MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE):
# Modern Meme with Image
text_line = pure_text_lines[0]
image_line = image_lines[0]
image = image_line.images[0]
full_image_url = image.url
caption = text_line.text
image = meme_generator.create_modern_meme_from_url(full_image_url, caption)
elif (big_marsey_lines_count == 1 and pure_text_lines_count == 2):
print(f"[{comment['id']}] CLASSIC_MEME_WITH_MARSEY")
if (random_float <= CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE):
# Classic Meme with big marsey
top_text_line = pure_text_lines[0]
bottom_text_line = pure_text_lines[1]
marsey_line = big_marsey_lines[0]
emoji = marsey_line.emojis[0].emoji
top_caption = top_text_line.text
bottom_caption = bottom_text_line.text
image = meme_generator.create_classic_meme_from_emoji(emoji, top_caption, bottom_caption)
elif (image_lines_count == 1 and pure_text_lines_count == 2):
print(f"[{comment['id']}] CLASSIC_MEME_WITH_IMAGE")
if (random_float <= CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE):
# Classic Meme with Image
top_text_line = pure_text_lines[0]
bottom_text_line = pure_text_lines[1]
image_line = image_lines[0]
image = image_line.images[0]
full_image_url = image.url
top_caption = top_text_line.text
bottom_caption = bottom_text_line.text
image = meme_generator.create_classic_meme_from_url(full_image_url, top_caption, bottom_caption)
elif (argument_lines_count + dialog_lines_count >= 2):
print(f"[{comment['id']}] WEBCOMIC")
if (random_float <= WEBCOMIC_TRIGGER_CHANCE):
panels : 'list[WebcomicPanel]' = []
for element_line in element_lines:
if element_line.is_dialogue_line:
caption = element_line.text
emoji = element_line.emojis[0].emoji
if len(caption) > 100:
in_background = True
else:
in_background = False
oneCharacterWebcomicPanel = OneCharacterWebcomicPanel(emoji, caption, in_background)
panels.append(oneCharacterWebcomicPanel)
elif element_line.is_argument_line:
if len(element_line.captions) == 2:
left_caption = element_line.captions[0].text
right_caption = element_line.captions[1].text
elif (len(element_line.captions) == 1):
left_caption = element_line.captions[0].text
right_caption = ""
else:
left_caption = ""
right_caption = ""
left_emoji = element_line.emojis[0].emoji
right_emoji = element_line.emojis[1].emoji
twoCharacterWebcomicPanel = TwoCharacterWebcomicPanel(left_emoji, left_caption, right_emoji, right_caption)
panels.append(twoCharacterWebcomicPanel)
else:
panels.append(TitleCardWebcomicPanel(element_line.text))
image = create_webcomic(panels)
print(asizeof.asized(image, detail=1).format())
if image != None:
print(f"[{comment['id']}] posting...")
image = add_watermark(image, comment['author']['username'])
user_id = 0 if comment['author'] == '👻' else comment['author']['id']
parent_comment_id = comment['id']
post_id = comment['post_id']
message = create_comment_message(chud, pizza, bird, is_marsey, mention=mention)
if not DRY_MODE:
automeme_comment_id = comment_with_image(message, image, comment['id'], comment['post_id'])
else:
automeme_comment_id = None
image.save(get_real_filename(f"dry/{comment['id']}.webp"))
Comment.create_new_comment(parent_comment_id, automeme_comment_id, session)
if post_id not in FREE_POSTS:
Post.increment_replies(post_id, session)
User.increase_number_of_comments(user_id, session)
else:
print(f"[{comment['id']}] is a free post.")
end = datetime.now()
print(end-begin)
else:
Comment.create_new_comment(comment['id'], None, session)
except BaseException as e:
print(f"YIKERINOS! GOT AN EXCEPTION: {e}")
traceback.print_exc()
def main_processing_task(rdrama : RDramaAPIInterface, session : Session): def main_processing_task(rdrama : RDramaAPIInterface, session : Session):
is_chudded = False #Do we have the chud award? is_chudded = False #Do we have the chud award?
can_communicate = True #Can we send any message at all? can_communicate = True #Can we send any message at all?
@ -324,179 +504,27 @@ def main_processing_task(rdrama : RDramaAPIInterface, session : Session):
if can_communicate: if can_communicate:
eligible_comments = get_eligible_comments(rdrama, session) eligible_comments = get_eligible_comments(rdrama, session)
for eligible_comment in eligible_comments: for eligible_comment in eligible_comments:
try: handle_comment(eligible_comment, rdrama, session, is_chudded, is_pizzad, is_birdsite, is_marseyed)
begin = datetime.now()
under_post_limit = Post.get_number_of_replies(eligible_comment['post_id'], session) < ALLOWED_COMMENTS_PER_POST
under_user_limit = User.get_number_of_comments(0 if eligible_comment['author'] == '👻' else eligible_comment['author']['id'], session) < ALLOWED_COMMENTS_PER_USER_PER_DAY
has_not_replied_to_comment = Comment.get_comment(eligible_comment['id'], session) is None
is_not_restricted_post = eligible_comment['post_id'] not in RESTRICTED_POSTS
if (not (under_post_limit and under_user_limit and has_not_replied_to_comment and is_not_restricted_post)):
continue
comment_text = eligible_comment['body']
directives = extract_directives(comment_text)
cleaned_comment_text = strip_markdown(comment_text)
if ("meme" in directives or eligible_comment['post_id'] in FREE_POSTS):
random_float = 0.0
else:
random_float = random()
comment_lines = cleaned_comment_text.split("\n")
comment_lines = [comment_line for comment_line in comment_lines if comment_line != ""]
element_lines = [TextLine(line) for line in comment_lines]
argument_lines_count, dialog_lines_count, text_lines_count, big_marsey_lines_count = 0,0,0,0
dialog_lines = list(filter(lambda a : a.is_dialogue_line, element_lines))
argument_lines = list(filter(lambda a : a.is_argument_line, element_lines))
pure_text_lines = list(filter(lambda a : a.is_pure_text_line, element_lines))
big_marsey_lines = list(filter(lambda a : a.is_big_marsey_line, element_lines))
image_lines = list(filter(lambda a : a.is_image_line, element_lines))
argument_lines_count = len(argument_lines)
dialog_lines_count = len(dialog_lines)
pure_text_lines_count = len(pure_text_lines)
big_marsey_lines_count = len(big_marsey_lines)
image_lines_count = len(image_lines)
image = None
if (dialog_lines_count == 2):
print(f"[{eligible_comment['id']}] SOY_VS_CHAD")
if (random_float <= SOY_VS_CHAD_TRIGGER_CHANGE):
#Soy vs Chad
line1 = dialog_lines[0]
line2 = dialog_lines[1]
emoji1 = line1.emojis[0].emoji
emoji2 = line2.emojis[0].emoji
caption1 = line1.text
caption2 = line2.text
image = meme_generator.create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2)
elif (big_marsey_lines_count == 1 and pure_text_lines_count == 1):
print(f"[{eligible_comment['id']}] MODERN_MEME_WITH_MARSEY")
if (random_float <= MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE):
# Modern Meme with Marsey
text_line = pure_text_lines[0]
marsey_line = big_marsey_lines[0]
marsey = marsey_line.emojis[0].emoji
caption = text_line.text
image = meme_generator.create_modern_meme_from_emoji(marsey, caption)
elif (image_lines_count == 1 and pure_text_lines_count == 1):
print(f"[{eligible_comment['id']}] MODERN_MEME_WITH_IMAGE")
if (random_float <= MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE):
# Modern Meme with Image
text_line = pure_text_lines[0]
image_line = image_lines[0]
image = image_line.images[0]
full_image_url = image.url
caption = text_line.text
image = meme_generator.create_modern_meme_from_url(full_image_url, caption)
elif (big_marsey_lines_count == 1 and pure_text_lines_count == 2):
print(f"[{eligible_comment['id']}] CLASSIC_MEME_WITH_MARSEY")
if (random_float <= CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE):
# Classic Meme with big marsey
top_text_line = pure_text_lines[0]
bottom_text_line = pure_text_lines[1]
marsey_line = big_marsey_lines[0]
emoji = marsey_line.emojis[0].emoji
top_caption = top_text_line.text
bottom_caption = bottom_text_line.text
image = meme_generator.create_classic_meme_from_emoji(emoji, top_caption, bottom_caption)
elif (image_lines_count == 1 and pure_text_lines_count == 2):
print(f"[{eligible_comment['id']}] CLASSIC_MEME_WITH_IMAGE")
if (random_float <= CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE):
# Classic Meme with Image
top_text_line = pure_text_lines[0]
bottom_text_line = pure_text_lines[1]
image_line = image_lines[0]
image = image_line.images[0]
full_image_url = image.url
top_caption = top_text_line.text
bottom_caption = bottom_text_line.text
image = meme_generator.create_classic_meme_from_url(full_image_url, top_caption, bottom_caption)
elif (argument_lines_count + dialog_lines_count >= 2):
print(f"[{eligible_comment['id']}] WEBCOMIC")
if (random_float <= WEBCOMIC_TRIGGER_CHANCE):
panels : 'list[WebcomicPanel]' = []
for element_line in element_lines:
if element_line.is_dialogue_line:
caption = element_line.text
emoji = element_line.emojis[0].emoji
if len(caption) > 100:
in_background = True
else:
in_background = False
oneCharacterWebcomicPanel = OneCharacterWebcomicPanel(emoji, caption, in_background)
panels.append(oneCharacterWebcomicPanel)
elif element_line.is_argument_line:
left_caption = element_line.captions[0].text
if len(element_line.captions) == 2:
right_caption = element_line.captions[1].text
else:
right_caption = ""
left_emoji = element_line.emojis[0].emoji
right_emoji = element_line.emojis[1].emoji
twoCharacterWebcomicPanel = TwoCharacterWebcomicPanel(left_emoji, left_caption, right_emoji, right_caption)
panels.append(twoCharacterWebcomicPanel)
elif element_line.is_pure_text_line:
panels.append(TitleCardWebcomicPanel(element_line.text))
image = create_webcomic(panels)
if image != None:
print(f"[{eligible_comment['id']}] posting...")
image = add_watermark(image, eligible_comment['author']['username'])
user_id = 0 if eligible_comment['author'] == '👻' else eligible_comment['author']['id']
parent_comment_id = eligible_comment['id']
post_id = eligible_comment['post_id']
message = create_comment_message(is_chudded, is_pizzad, is_birdsite, is_marseyed)
if not DRY_MODE:
automeme_comment_id = comment_with_image(message, image, eligible_comment['id'], eligible_comment['post_id'])
else:
automeme_comment_id = None
image.save(get_real_filename(f"dry/{eligible_comment['id']}.webp"))
Comment.create_new_comment(parent_comment_id, automeme_comment_id, session)
if post_id not in FREE_POSTS:
Post.increment_replies(post_id, session)
User.increase_number_of_comments(user_id, session)
else:
print(f"[{eligible_comment['id']}] is a free post.")
end = datetime.now()
print(end-begin)
else:
Comment.create_new_comment(eligible_comment['id'], None, session)
except BaseException as e:
print(f"YIKERINOS! GOT AN EXCEPTION: {e}")
traceback.print_exc()
ScriptCall.register(session)
def comment_chunk(time : datetime, api: RDramaAPIInterface): def comment_chunk(time : datetime, api: RDramaAPIInterface):
current_time_cur = int(time.timestamp()) # int(time.time() - 60*60*8) current_time_cur = int(time.timestamp()) # int(time.time() - 60*60*8)
comments = [] comments = []
pages = 0
while True: while True:
pages+=1
res = api.get_comments(sort="old", lower_bound=current_time_cur) res = api.get_comments(sort="old", lower_bound=current_time_cur)
if len(res['data']) == 0: if len(res['data']) == 0:
break break
if pages > MAX_PAGES_TO_SCAN:
print("WARNING: Had to drop some pages. Can't make an omelette without cracking a few eggs")
break
comments = comments + res['data'] comments = comments + res['data']
current_time_cur = res['data'][-1]['created_utc'] current_time_cur = res['data'][-1]['created_utc']
return comments return comments
if __name__ == "__main__":
print(f"======= RUNNING AT {datetime.now().hour}:{datetime.now().minute} ======= ") def get_rdrama():
global AUTOMEME_ID, OPERATOR_ID
if TEST_MODE: if TEST_MODE:
website = "localhost" website = "localhost"
auth = TEST_AUTH_TOKEN auth = TEST_AUTH_TOKEN
@ -504,7 +532,6 @@ if __name__ == "__main__":
timeout = 1 timeout = 1
AUTOMEME_ID = 6 AUTOMEME_ID = 6
OPERATOR_ID = 9 OPERATOR_ID = 9
ACTUALLY_CALL_OPEN_AI = False
else: else:
website = "rdrama.net" website = "rdrama.net"
with open(get_real_filename("rdrama_auth_token"), "r") as f: with open(get_real_filename("rdrama_auth_token"), "r") as f:
@ -512,6 +539,11 @@ if __name__ == "__main__":
https = True https = True
timeout = 10 timeout = 10
rdrama = RDramaAPIInterface(auth, website, timeout, https=https) rdrama = RDramaAPIInterface(auth, website, timeout, https=https)
return rdrama
if __name__ == "__main__":
print(f"======= RUNNING AT {datetime.now().hour}:{datetime.now().minute} ======= ")
rdrama = get_rdrama()
#Set up fail safe #Set up fail safe
def exitfunc(): def exitfunc():

View File

@ -10,9 +10,10 @@ from utils import get_real_filename
from image_utils import ImageText from image_utils import ImageText
from os.path import exists from os.path import exists
from random import choice from random import choice
from pympler import tracker
HIGHLIGHT_MODE = False HIGHLIGHT_MODE = False #Adds a square around the region where text is being put in a text box. Useful for debugging.
CAPTION_FILENAME = "watermark_captions.txt" CAPTION_FILENAME = "watermark_captions.txt" #Name of the file containing watermark captions.
class ColorScheme: class ColorScheme:
BLACK = 0 BLACK = 0
@ -28,7 +29,6 @@ def create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2):
MIDDLE_MARGIN_ROW = 20 MIDDLE_MARGIN_ROW = 20
TEXT_ROW = 100 TEXT_ROW = 100
BOTTOM_MARGIN_ROW = 80 BOTTOM_MARGIN_ROW = 80
total_image_size_x = 2*CONTENT_COLUMN + LEFT_MARGIN_COLUMN + RIGHT_MARGIN_COLUMN + MIDDLE_MARGIN_COLUMN 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 total_image_size_y = TOP_MARGIN_ROW + IMAGE_ROW + MIDDLE_MARGIN_ROW + TEXT_ROW + BOTTOM_MARGIN_ROW
@ -116,9 +116,12 @@ def create_modern_meme_from_emoji(emoji: str, caption: str):
return create_modern_meme(base, caption) return create_modern_meme(base, caption)
class WebcomicPanel(): class WebcomicPanel():
PANEL_SIZE = 400 '''
FONT = "impact.ttf" A panel in a webcomic.
COLOR = ColorScheme.WHITE_WITH_BLACK_BORDER '''
PANEL_SIZE = 400 #Size of a webcomic panel.
FONT = "impact.ttf" #The font to use.
COLOR = ColorScheme.WHITE_WITH_BLACK_BORDER #The color of the font.
def __init__(self): def __init__(self):
pass pass
@ -130,6 +133,9 @@ class WebcomicPanel():
return add_text_box(base, caption, region_size, coordinates, align=align, font=self.FONT, color=self.COLOR, init_font_size=int(self.PANEL_SIZE/10)) return add_text_box(base, caption, region_size, coordinates, align=align, font=self.FONT, color=self.COLOR, init_font_size=int(self.PANEL_SIZE/10))
class OneCharacterWebcomicPanel(WebcomicPanel): class OneCharacterWebcomicPanel(WebcomicPanel):
'''
Panel with one character talking.
'''
def __init__(self, emoji, caption, words_in_background): def __init__(self, emoji, caption, words_in_background):
self.emoji = emoji self.emoji = emoji
self.caption = caption self.caption = caption
@ -155,6 +161,9 @@ class OneCharacterWebcomicPanel(WebcomicPanel):
return add_border_to_image(base) return add_border_to_image(base)
class TwoCharacterWebcomicPanel(WebcomicPanel): class TwoCharacterWebcomicPanel(WebcomicPanel):
'''
Panel with two characters talking.
'''
def __init__(self, left_emoji, left_caption, right_emoji, right_caption): def __init__(self, left_emoji, left_caption, right_emoji, right_caption):
self.left_emoji = left_emoji self.left_emoji = left_emoji
self.left_caption = left_caption self.left_caption = left_caption
@ -204,6 +213,9 @@ class TwoCharacterWebcomicPanel(WebcomicPanel):
return add_border_to_image(base) return add_border_to_image(base)
class TitleCardWebcomicPanel(WebcomicPanel): class TitleCardWebcomicPanel(WebcomicPanel):
'''
A caption in a webcomic.
'''
def __init__(self, caption): def __init__(self, caption):
self.caption = caption self.caption = caption
@ -221,6 +233,7 @@ def create_webcomic(layout : 'list[WebcomicPanel]'):
image = AnimatedImage.new((total_image_x_size, total_image_y_size)) image = AnimatedImage.new((total_image_x_size, total_image_y_size))
for i in range(len(layout)): for i in range(len(layout)):
print(i)
panel = layout[i] panel = layout[i]
x = i%2 x = i%2
y = math.floor(i/2) y = math.floor(i/2)
@ -228,6 +241,26 @@ def create_webcomic(layout : 'list[WebcomicPanel]'):
return image return 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): 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):
'''
Adds a text box, where the size of the text scales to fit the box as closely as possible.
base: the image to put the text in.
caption: the text
region size: the size of the text box.
coordinates: the position of the text box.
font: name of the font file to use (defaults to arial)
init_font_size: the largest font size that will be used.
align: sets the alignment of the text.
Horizontal Alignments:
t: top
b: bottom
cv: center
Vertical Alignments:
l: left
r: right
ch: center
color: what colorscheme to use.
'''
font = get_real_filename(font) font = get_real_filename(font)
if caption == "": if caption == "":
@ -288,6 +321,9 @@ def add_text_box(base : Image, caption : str, region_size : 'tuple[int, int]', c
return add_text_box(base, caption, region_size, coordinates, font=font, init_font_size=init_font_size-1, align=align, color=color) return 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"): def add_text(base : Image, caption : str, region_size : 'tuple[int, int]', coordinates : 'tuple[int, int]', font : str= "arial.ttf"):
'''
Very simple interface for adding text.
'''
font = get_real_filename(font) font = get_real_filename(font)
print(font) print(font)
if caption == "": if caption == "":
@ -298,6 +334,9 @@ def add_text(base : Image, caption : str, region_size : 'tuple[int, int]', coord
return base.paste(AnimatedImage.from_image(line_image.image), coordinates) return base.paste(AnimatedImage.from_image(line_image.image), coordinates)
def add_watermark(image : Image, name_of_other_creator): def add_watermark(image : Image, name_of_other_creator):
'''
Adds the rdrama watermark to the bottom of the image, crediting the other creator of the image.
'''
global watermark_captions global watermark_captions
WATERMARK_HEIGHT = int(0.05 * image.height) WATERMARK_HEIGHT = int(0.05 * image.height)
image_size_x, image_size_y = image.size image_size_x, image_size_y = image.size
@ -317,6 +356,10 @@ def add_watermark(image : Image, name_of_other_creator):
return base return base
def center_and_paste(base : Image, to_paste : Image, coordinates: 'tuple[int, int]', box_size : 'tuple[int, int]') -> 'AnimatedImage': def center_and_paste(base : Image, to_paste : Image, coordinates: 'tuple[int, int]', box_size : 'tuple[int, int]') -> 'AnimatedImage':
'''
Centers to_paste in a box whose upper-left corder is "coordinates", and with size box_size.
'''
to_paste = to_paste.fit(box_size) to_paste = to_paste.fit(box_size)
image_size_x, image_size_y = to_paste.size image_size_x, image_size_y = to_paste.size
@ -335,6 +378,9 @@ def center_and_paste(base : Image, to_paste : Image, coordinates: 'tuple[int, in
return base.paste(to_paste, (x, y)) return base.paste(to_paste, (x, y))
def add_border_to_image(image : Image, thickness : int = 5): def add_border_to_image(image : Image, thickness : int = 5):
'''
Adds a black border to an image.
'''
inner_image_x_size, inner_image_y_size = image.size 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 outer_image_x_size, outer_image_y_size = inner_image_x_size + 2*thickness, inner_image_y_size + 2*thickness
outside_image = AnimatedImage.new((outer_image_x_size,outer_image_y_size), color=(0,0,0)) outside_image = AnimatedImage.new((outer_image_x_size,outer_image_y_size), color=(0,0,0))
@ -343,6 +389,9 @@ def add_border_to_image(image : Image, thickness : int = 5):
return outside_image return outside_image
def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage': def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage':
'''
Gets an emoji from rdrama. If there is a "!" in the emoji name, it will be flipped.
'''
cleaned_emoji_name : str = emoji_name cleaned_emoji_name : str = emoji_name
should_flip = False should_flip = False
if '!' in emoji_name: if '!' in emoji_name:
@ -360,6 +409,9 @@ def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage':
return image return image
def get_image_file_from_url(url) -> 'AnimatedImage': def get_image_file_from_url(url) -> 'AnimatedImage':
'''
Gets the image at the given address.
'''
try: try:
r = requests.get(url) r = requests.get(url)
image_file = io.BytesIO(r.content) image_file = io.BytesIO(r.content)
@ -367,7 +419,11 @@ def get_image_file_from_url(url) -> 'AnimatedImage':
return AnimatedImage.from_image(im) return AnimatedImage.from_image(im)
except: except:
print(f"ERROR GETTING FILE FROM {url}") print(f"ERROR GETTING FILE FROM {url}")
def parse_caption_file(filename): def parse_caption_file(filename):
'''
Opens a file of strings and returns them.
'''
if not exists(filename): if not exists(filename):
return [] return []
to_return = [] to_return = []
@ -376,9 +432,12 @@ def parse_caption_file(filename):
to_return.append(id.strip()) to_return.append(id.strip())
return to_return return to_return
watermark_captions = parse_caption_file(get_real_filename(CAPTION_FILENAME))
class AnimatedImage(): class AnimatedImage():
'''
Basically a list of images, meant to be animated.
'''
def __init__(self, frames : 'list[Image.Image]'): def __init__(self, frames : 'list[Image.Image]'):
if len(frames) > 100: if len(frames) > 100:
self.frames = frames[0:100] self.frames = frames[0:100]
@ -387,45 +446,70 @@ class AnimatedImage():
@property @property
def size(self) -> 'Tuple[int, int]': def size(self) -> 'Tuple[int, int]':
'''
Size of the image.
'''
return self.frames[0].size return self.frames[0].size
@property @property
def height(self) -> int: def height(self) -> int:
'''
Height of the image.
'''
return self.frames[0].size[1] return self.frames[0].size[1]
@property @property
def width(self) -> int: def width(self) -> int:
'''
Width of the image.
'''
return self.frames[0].size[0] return self.frames[0].size[0]
def flip(self) -> 'AnimatedImage': def flip(self) -> 'AnimatedImage':
new_frames = [] '''
for frame in self.frames: Flips image horizontally.
#frame.show() '''
new_frame = ImageOps.mirror(frame)
#new_frame.show()
new_frames.append(new_frame)
return AnimatedImage(new_frames)
def save(self, filename) -> None: return self.perform_transform_for_all_images(lambda image : ImageOps.mirror(image))
def save(self, filename : str) -> None:
'''
Saves the image to the filename
'''
self.frames[0].save(f"{filename}", save_all = True, append_images = self.frames[1:], duration = 100, loop=0) self.frames[0].save(f"{filename}", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
def get_binary(self) -> bytes: def get_binary(self) -> bytes:
'''
Converts the image to binary.
'''
output = io.BytesIO() output = io.BytesIO()
self.frames[0].save(output, format="webp", save_all = True, append_images = self.frames[1:], duration = 100, loop=0) self.frames[0].save(output, format="webp", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
return output.getvalue() return output.getvalue()
def get_binary_gif(self) -> bytes:
output = io.BytesIO()
self.frames[0].save(output, format="gif", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
return output.getvalue()
def from_image(image: Image.Image) -> 'AnimatedImage': def from_image(image: Image.Image) -> 'AnimatedImage':
'''
Converts a run of the mill PIL image to an AnimatedImage.
'''
frames = [] frames = []
for image_frame in ImageSequence.Iterator(image): for image_frame in ImageSequence.Iterator(image):
frames.append(image_frame.copy()) frames.append(image_frame.copy())
return AnimatedImage(frames) return AnimatedImage(frames)
def paste(self, other : 'AnimatedImage', position : 'Tuple[int, int]') -> 'AnimatedImage': def paste(self, other : 'AnimatedImage', position : 'Tuple[int, int]') -> 'AnimatedImage':
'''
Pastes one image onto another. Tries to do it without breaking the loop.
'''
num_self_frames = len(self.frames) num_self_frames = len(self.frames)
num_other_frames = len(other.frames) num_other_frames = len(other.frames)
new_frames = [] new_frames = []
for i in range(get_ideal_number_of_frames(num_self_frames, num_other_frames)): frames_to_create = min(get_ideal_number_of_frames(num_self_frames, num_other_frames), 100)
for i in range(frames_to_create):
self_frame = self.frames[i%num_self_frames] self_frame = self.frames[i%num_self_frames]
other_frame = other.frames[i%num_other_frames] other_frame = other.frames[i%num_other_frames]
@ -439,6 +523,9 @@ class AnimatedImage():
return AnimatedImage(new_frames) return AnimatedImage(new_frames)
def new(size : 'Tuple[int, int]', color=(255,255,255)) -> 'AnimatedImage': def new(size : 'Tuple[int, int]', color=(255,255,255)) -> 'AnimatedImage':
'''
Creates a blank AnimatedImage.
'''
base = Image.new(mode="RGB", size=size, color=color) base = Image.new(mode="RGB", size=size, color=color)
return AnimatedImage.from_image(base) return AnimatedImage.from_image(base)
@ -446,10 +533,7 @@ class AnimatedImage():
''' '''
Shrinks the image while preserving it's aspect ratio, such that the new image has the same ratio but is contained in the given box. Shrinks the image while preserving it's aspect ratio, such that the new image has the same ratio but is contained in the given box.
''' '''
new_frames = [] return self.perform_transform_for_all_images(lambda image : ImageOps.contain(image, region))
for frame in self.frames:
new_frames.append(ImageOps.contain(frame, region))
return AnimatedImage(new_frames)
def stretch(self, region : 'Tuple[int, int]') -> 'AnimatedImage': def stretch(self, region : 'Tuple[int, int]') -> 'AnimatedImage':
''' '''
@ -507,6 +591,9 @@ def get_leftover_primes(a_, b_):
leftover_primes.append(i) leftover_primes.append(i)
return leftover_primes return leftover_primes
watermark_captions = parse_caption_file(get_real_filename(CAPTION_FILENAME))
# shooting = get_emoji_from_rdrama("!marseyshooting") # shooting = get_emoji_from_rdrama("!marseyshooting")
# shooting2 = get_emoji_from_rdrama("marseydeterminedgun") # shooting2 = get_emoji_from_rdrama("marseydeterminedgun")
@ -525,7 +612,7 @@ def get_leftover_primes(a_, b_):
# TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"), # TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"),
# OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False) # OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False)
# ]).save("webcomic.webp") # ]).save("webcomic.webp")
create_modern_meme_from_url("https://media.giphy.com/media/gYkga3bZav66I/giphy.webp", "me when i see a black person (i am extremely racist)").save("racism.webp") #create_modern_meme_from_url("https://media.giphy.com/media/gYkga3bZav66I/giphy.webp", "me when i see a black person (i am extremely racist)").save("racism.webp")
#create_classic_meme_from_url("https://rdrama.net/images/16619117705921352.webp", "I left out 'hiking' intentionally since it's outdoor hobby 101. But even if he's trying to attract a homebody some kind of interest would be good to have. Like home brewing, art, instrument, or god forbid tabletop gaming.", "This just screams most boring, generic, codependent 'man' ever. I need a wife and I'll be happy to do whatever you want to do 24/7 because I have no personality, friends, or interests").save("classic.webp") #create_classic_meme_from_url("https://rdrama.net/images/16619117705921352.webp", "I left out 'hiking' intentionally since it's outdoor hobby 101. But even if he's trying to attract a homebody some kind of interest would be good to have. Like home brewing, art, instrument, or god forbid tabletop gaming.", "This just screams most boring, generic, codependent 'man' ever. I need a wife and I'll be happy to do whatever you want to do 24/7 because I have no personality, friends, or interests").save("classic.webp")
#create_modern_meme_from_url("https://rdrama.net/images/16617243111639555.webp", "It really does, especially with that weird lighting. ANd what's up with the subset of troons with Himmler eyes?").save("modern_image.webp") #create_modern_meme_from_url("https://rdrama.net/images/16617243111639555.webp", "It really does, especially with that weird lighting. ANd what's up with the subset of troons with Himmler eyes?").save("modern_image.webp")
#create_classic_meme_from_emoji("marseycock", "I WANT TO PUT MY DICK", "INSIDE OF MARSEY").save("classic_with_emoji.webp") #create_classic_meme_from_emoji("marseycock", "I WANT TO PUT MY DICK", "INSIDE OF MARSEY").save("classic_with_emoji.webp")

View File

@ -0,0 +1,46 @@
from io import BytesIO
import tweepy
from automeme import TextLine, get_rdrama, strip_markdown
import utils
import json
from meme_generator import get_image_file_from_url
def load_key_from_file(filename : str) -> str:
with open(utils.get_real_filename(filename), "r") as f:
return f.read()
consumer_key = load_key_from_file("twitter_api_key")
consumer_secret = load_key_from_file("twitter_api_secret")
access_token = load_key_from_file("twitter_access_token")
access_token_secret = load_key_from_file("twitter_access_token_secret")
auth = tweepy.OAuth1UserHandler(
consumer_key,
consumer_secret,
access_token,
access_token_secret
)
api = tweepy.API(auth)
rdrama = get_rdrama()
best = rdrama.get_comments(user = "automeme", sort = "top", t="day")['data'][0]
print(json.dumps(best, indent=4))
comment_text = best['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 = [TextLine(line) for line in comment_lines]
for i in element_lines:
print(i)
image = element_lines[-1].images[0]
print(image.url)
animated_image = get_image_file_from_url(image.url)
file = BytesIO(initial_bytes=animated_image.get_binary_gif())
media = api.media_upload(filename = "foo.gif", file=file, chunked=True, wait_for_async_finalize = True)
media_id = media.media_id_string
api.update_status("#meme", media_ids=[media_id])