Compare commits

...

5 Commits

Author SHA1 Message Date
HeyMoon 2893fd4bbc pre fsdfsd 2022-11-27 13:59:09 -06:00
HeyMoon 5c868d7aa7 add darrellposting 2022-10-22 16:36:52 -05:00
HeyMoon 5362f8374c updates 2022-10-19 19:42:57 -05:00
HeyMoon 3ba8fb6d50 Resize images 2022-09-04 14:52:55 -05:00
HeyMoon e26e7056cd getting it running for the first time 2022-08-28 17:38:40 -05:00
6 changed files with 681 additions and 226 deletions

18
.gitignore vendored
View File

@ -1,3 +1,19 @@
__pycache__/image_utils.cpython-39.pyc
emoji_cache/
*.ttf
dry/
*.ttf
*.pyc
__pycache__/
rdrama_auth_token
twitter_access_token
twitter_access_token_secret
twitter_api_key
twitter_api_secret
twitter_bearer_token
twitter_client_id
twitter_client_secret
*.db
*.db-journal
log
*.json

View File

@ -139,5 +139,24 @@ class CommentString(Base):
'''
Mostly for keeping track of how long it's been since the last run, so we can run things without worrying about set number of pages
'''
class ScriptCalls(Base):
pass
class ScriptCall(Base):
__tablename__ = "script_call"
id = Column(Integer, primary_key = True)
time = Column(DateTime)
def register(session : Session):
sc = ScriptCall(time = datetime.now())
session.add(sc)
def get_time_of_last_run(session) -> datetime:
stmt = sqlalchemy.select(ScriptCall).order_by(ScriptCall.time.desc()).limit(1)
sc = session.execute(stmt).scalar_one_or_none()
if (sc == None):
return datetime.now()
else:
return sc.time

View File

@ -1,42 +1,105 @@
import base64
import io
import json
from random import random
from random import choice, choices, random
import re
import traceback
from typing import Callable, TypeVar
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 datetime import datetime
from datetime import datetime, timedelta
from os.path import exists, join, realpath, split
from threading import Timer
from BotModels import Base, Comment, CommentString, Post, User
from BotModels import Base, Comment, CommentString, Post, ScriptCall, User
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
import os
from markdown import markdown
from bs4 import BeautifulSoup
from utils import get_real_filename
from pympler import tracker, asizeof, classtracker
TEST_MODE = True
ANTISPAM_MESSAGES = [
'i have a meme for u :marseyshy:',
'anon -- can you suck my penis please?',
'uwu i love memes',
'~ oh... dramatardchan -- i wish ud stop talking about trains',
'hey dramatardchan, its ok if u have autism',
'its okay silly ~ i know u are retarded',
'on rdrama someone told me to sneed, what does that mean?',
'what if no-one likes my shitposts :marseycry:',
'a ha ha ha, that\'s very funny dramatardchan',
'im going to a party ~ no you dont have to come ~ love u',
'oh dramatardchan, your such a special boy',
'omg hes talking about "trannys" again, im so wet down there',
'idk what that means but i still love u',
'i love u but pls dont send me penis pictures, its kinda gross >.<',
'aha thats very funny, thank u',
'hey dramatardchan... i was thinking... maybe u could take a shower... if u want',
'i love u becuz ur so sweet, even if you are kind of a pushover, dramatardchan',
'maybe u should delete ur account - not trying to be mean tho',
'look at this cute penis picture i found on twitter... no, its ok, u dont have to show me urs',
'FUCK YOU. I HAVE HAD IT UP TO HERE WITH YOUR FUCKING DRAMA BULLSHIT. FUCK. OFF.',
'idk what u mean by mommy milkers 😭 im not ur mommy pls stop saying that',
'goldstein sama is so cute ~ what a cute nose... urs is fine to tho'
]
DARRELL_QUOTES = [
":#marseyobjection: \nFor the record, your honor, I do not consent to being called that name, nor do I know anyone by that name",
":#brooksannoyed: \nI see what you're doing. You must think you're slick.",
":#marseyobjection: \nThe jury deserves to know, your honor.",
":#marseyjurisdiction: \nCan we please address :marseyjurisdiction:?",
":#marseygrouns:",
":#marseygrouns:\n:marseygrouns: for the substain?",
":#marseygrouns:\nWho would you say is the plantiff in this matter?",
":#marseygrouns:\nWould it be fair to say that the State of Wisconsin is not a living, breathing person?",
":#marseygrouns:\nThe defense calls the State of Wisconsin.",
":#brooksannoyed:\nHow can you even call yourself a judge?",
":#brooksannoyed:\n\*takes off shirt for some reason\*",
":#brooksannoyed:\nMind boggling. Ming boggling.",
":#brooksannoyed:\nSHE SAID SHE WAS 18!",
":#marseyrelevancy:\nuhh what's the :marseyrelevancy:?",
":#marseyrelevancy:\n[\*talks for fifty minutes straight\*](https://rdrama.net/post/113381/marseygrounsmarseyjudge-darrel-brooks-entire-50minute-long)",
":#marseyrelevancy:\nI see what you're doing. Trying to be slick.",
":#brooksannoyed:\nYour honor, that's a load of crap.",
":#marseytakit:\nIs that lawfully law?",
":#marseytakit:\nIs that a :marseytakit:?",
":#brooksannoyed:\nI AM A GROWN MAN WITH GROWN KIDS AINT NOBODY TELL ME WHEN TO TALK",
":#marseybiast:\nIs this a common law court or an admiralty court?",
":#marseybiast:\nSeems to me like a lot of your answers are coached.",
":#marseyjurisdiction:\nWe STILL have yet to discuss :#marseyjurisdiction:.",
":#marseyjurisdiction:\nWhat da bid'ness?",
":#marseyjurisdiction:\nI need a signed affidavit of your oath of office."
]
DARRELL_KEYWORDS = [
"brooks",
"darrell"
]
DARRELL_DISCLAIMER = "_I am not Darrell Brooks, I am a third party intervener, here on behalf of my client_"
LLM_KEYWORDS = [
"llm",
"landlordmessiah",
"landlord messiah"
]
TEST_MODE = False
DRY_MODE = False
TEST_AUTH_TOKEN = "ED3eURMKP9FKBFbi-JUxo8MPGWkEihuyIlAUGtVL7xwx0NEy4Nf6J_mxWYTPgAQx1iy1X91hx7PPHyEBS79hvKVIy5DMEzOyAe9PAc5pmqSJlLGq_-ROewMwFzGrqer4"
BETA_MODE = False
TEST_AUTH_TOKEN = "2PR7FAqx0UVM71HayM33p1OTy_zFGOK1rJlCNi1qzvFJqQGK6NOm7I-mSfYjZgs02FU_LqLTPqDa8KN-LUiGSJ1idFfE5jKnG3DUk9rQjn_ZrAtG_VU9GK5F_EGmixg1"
MINUTES_BEFORE_FORCED_SHUTDOWN = 10
DB_FILENAME = "automeme_database.db"
PAGES_TO_SCAN = 5
MAX_PAGES_TO_SCAN = 10
AUTOMEME_ID = 13427
ALLOWED_COMMENTS_PER_POST = 20
ALLOWED_COMMENTS_PER_USER_PER_DAY = 20
MESSAGE = "i have a meme for u :marseyshy: \nPROTIP: I reply randomly. Put !!meme in your message to make sure I reply"
SOY_VS_CHAD_TRIGGER_CHANGE = 1.0
MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE = 1.0 #0.1
MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE = 1.0
CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE = 1.0 #0.5
CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE = 1.0
WEBCOMIC_TRIGGER_CHANCE = 1.0
SOY_VS_CHAD_TRIGGER_CHANGE = 0.01
MODERN_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.001
MODERN_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.005
CLASSIC_MEME_WITH_MARSEY_TRIGGER_CHANGE = 0.002
CLASSIC_MEME_WITH_IMAGE_TRIGGER_CHANGE = 0.001
WEBCOMIC_TRIGGER_CHANCE = 0.01
DARRELL_CHANCE = 1.0
FREE_POSTS = [6]
FREE_POSTS = [6, 97416, 98286]
RESTRICTED_POSTS = [5, 16583, 75878, 35835]
EMOJI_REGEX = r":[#!a-zA-Z0-9]*:"
@ -87,7 +150,7 @@ class TextLine:
@property
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
def is_pure_text_line(self):
@ -101,6 +164,12 @@ class TextLine:
def is_image_line(self):
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():
pass
@ -133,7 +202,7 @@ class Emoji(TextElement):
def __repr__(self) -> str:
return f"Emoji({self.emoji}, big={self.big})"
def get_text_only(text_elements : list[TextElement]) -> str:
def get_text_only(text_elements : 'list[TextElement]') -> str:
text = [i.text for i in text_elements if isinstance(i, Text)]
return " ".join(text)
@ -187,34 +256,56 @@ def strip_markdown(markdown_string):
text = re.sub("@(.*?)\s", "", text)
text = re.sub("!slots.*?\s", "", text)
text = re.sub("(?i)detrans lives matter", "", text)
text = re.sub("(?i)trans lives matter", "", text)
return text
return text.strip()
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 comment['author'] == '👻' or not comment['author']['id'] == AUTOMEME_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(0 if comment['author'] == '👻' else 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
last_time = ScriptCall.get_time_of_last_run(session)
current_time = int(datetime.now().timestamp())
begin = datetime.now()
comments = comment_chunk(last_time, rdrama) #rdrama.get_comments(number_of_pages=3, upper_bound=current_time, lower_bound=last_time, sort="old")['data']
end = datetime.now()
print(end-begin)
#comments = [comment for comment in comments if comment['created_utc'] > last_time]
for comment in comments:
user_id = 0 if comment['author'] == '👻' else comment['author']['id']
comment_id = comment['id']
text = comment['body']
time = datetime.fromtimestamp(comment['created_utc'])
CommentString.add_comment(comment_id, user_id, time, text, session)
print([comment['id'] for comment in comments])
comments = [comment for comment in comments if not comment['is_bot']] #No bots
print([comment['id'] for comment in comments])
comments = [comment for comment in comments if comment['author'] == '👻' or not comment['author']['id'] == AUTOMEME_ID] #Don't reply to self
print([comment['id'] for comment in comments])
comments = [comment for comment in comments if Post.get_number_of_replies(comment['post_id'], session) < ALLOWED_COMMENTS_PER_POST] #Don't spam posts
print([comment['id'] for comment in comments])
comments = [comment for comment in comments if User.get_number_of_comments(0 if comment['author'] == '👻' else comment['author']['id'], session) < ALLOWED_COMMENTS_PER_USER_PER_DAY] #Don't spam users
print([comment['id'] for comment in comments])
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
if BETA_MODE:
comments = [comment for comment in comments if comment['post_id'] in FREE_POSTS]
print([comment['id'] for comment in comments])
comments = remove_duplicates(comments) #Remove the duplicates
print([comment['id'] for comment in comments])
ScriptCall.register(session)
session.commit()
return comments
T = TypeVar('T')
def lambda_count(list : list[T], predicate : 'Callable[[T], bool]' ):
def lambda_count(list : 'list[T]', predicate : 'Callable[[T], bool]' ):
return sum(1 for i in list if predicate(i))
def get_full_rdrama_image_url(partial_url) -> str:
@ -228,12 +319,16 @@ def extract_directives(string : str):
list = [i.lower()[2:] for i in 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):
return ":marseyshy:"
message = MESSAGE
message = choice(ANTISPAM_MESSAGES) +"\n"
if (mention != None):
message += f"@{mention}\n"
if (chud):
message += "\ntrans lives matter"
if (pizza):
@ -241,6 +336,200 @@ def create_comment_message(chud: bool, pizza: bool, bird : bool, marsey : bool):
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)
elif comment_contains_keyword(comment_text, DARRELL_KEYWORDS):
if random_float <= DARRELL_CHANCE:
message = choice(DARRELL_QUOTES)
message += "\n\n"
message += DARRELL_DISCLAIMER
post_id = comment['post_id']
user_id = 0 if comment['author'] == '👻' else comment['author']['id']
new_comment_id = rdrama.reply_to_comment_easy(comment['id'], post_id, message)['id']
Comment.create_new_comment(comment['id'], new_comment_id, session)
Post.increment_replies(post_id, session)
User.increase_number_of_comments(user_id, session)
elif comment_contains_keyword(comment_text, LLM_KEYWORDS):
message = "![](/images/1669576960061573.webp)"
post_id = comment['post_id']
user_id = 0 if comment['author'] == '👻' else comment['author']['id']
new_comment_id = rdrama.reply_to_comment_easy(comment['id'], post_id, message)['id']
Comment.create_new_comment(comment['id'], new_comment_id, session)
Post.increment_replies(post_id, session)
User.increase_number_of_comments(user_id, session)
else:
Comment.create_new_comment(comment['id'], None, session)
except BaseException as e:
print(f"YIKERINOS! GOT AN EXCEPTION: {e}")
traceback.print_exc()
def comment_contains_keyword(comment, keywords):
comment_words = comment.lower().split(" ")
return len(set.intersection(set(comment_words), set(keywords))) != 0
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?
@ -269,175 +558,41 @@ def main_processing_task(rdrama : RDramaAPIInterface, session : Session):
if automeme_information['is_banned']:
print("We are banned. STOP.")
can_communicate = False
if not can_communicate:
print("I can't communicate. Why????")
if can_communicate:
eligible_comments = get_eligible_comments(rdrama, session)
for eligible_comment in eligible_comments:
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
handle_comment(eligible_comment, rdrama, session, is_chudded, is_pizzad, is_birdsite, is_marseyed)
def comment_chunk(time : datetime, api: RDramaAPIInterface):
current_time_cur = int(time.timestamp()) # int(time.time() - 60*60*8)
comments = []
pages = 0
while True:
pages+=1
res = api.get_comments(sort="old", lower_bound=current_time_cur)
if len(res['data']) == 0:
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']
current_time_cur = res['data'][-1]['created_utc']
comment_text = eligible_comment['body']
directives = extract_directives(comment_text)
cleaned_comment_text = strip_markdown(comment_text)
return comments
if ("meme" in directives):
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(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)
if __name__ == "__main__":
TEST_AUTH_TOKEN = "ED3eURMKP9FKBFbi-JUxo8MPGWkEihuyIlAUGtVL7xwx0NEy4Nf6J_mxWYTPgAQx1iy1X91hx7PPHyEBS79hvKVIy5DMEzOyAe9PAc5pmqSJlLGq_-ROewMwFzGrqer4"
print(f"======= RUNNING AT {datetime.now().hour}:{datetime.now().minute} ======= ")
def get_rdrama():
global AUTOMEME_ID, OPERATOR_ID
if TEST_MODE:
website = "localhost"
auth = TEST_AUTH_TOKEN
https = False
timeout = 1
AUTOMEME_ID = 7
AUTOMEME_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:
@ -445,6 +600,11 @@ if __name__ == "__main__":
https = True
timeout = 10
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
def exitfunc():
@ -460,4 +620,4 @@ if __name__ == "__main__":
with Session(engine) as session:
main_processing_task(rdrama, session)
session.commit()
timer.cancel()
timer.cancel()

View File

@ -1,6 +1,5 @@
import math
from tkinter import UNITS
from typing import Tuple, Union
from typing import Callable, Tuple, Union
from PIL import Image
from PIL import ImageDraw, ImageSequence
from PIL import ImageFont
@ -11,9 +10,10 @@ from utils import get_real_filename
from image_utils import ImageText
from os.path import exists
from random import choice
from pympler import tracker
HIGHLIGHT_MODE = False
CAPTION_FILENAME = "watermark_captions.txt"
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" #Name of the file containing watermark captions.
class ColorScheme:
BLACK = 0
@ -29,7 +29,6 @@ def create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2):
MIDDLE_MARGIN_ROW = 20
TEXT_ROW = 100
BOTTOM_MARGIN_ROW = 80
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
@ -79,7 +78,7 @@ def create_classic_meme(image: 'AnimatedImage', top_caption : str, bottom_captio
return image
def create_classic_meme_from_url(url : str, top_caption : str, bottom_caption : str) -> 'AnimatedImage':
return create_classic_meme(get_image_file_from_url(url), top_caption, bottom_caption)
return create_classic_meme(get_image_file_from_url(url).stretch_maintain_aspect_ratio(500), top_caption, bottom_caption)
def create_classic_meme_from_emoji(emoji : str, top_caption : str, bottom_caption : str):
EMOJI_SIZE = 600
@ -102,7 +101,7 @@ def create_modern_meme(image : 'AnimatedImage', caption : str) -> 'AnimatedImage
return base
def create_modern_meme_from_url(url : str, caption : str):
return create_modern_meme(get_image_file_from_url(url), caption)
return create_modern_meme(get_image_file_from_url(url).stretch_maintain_aspect_ratio(500), caption)
def create_modern_meme_from_emoji(emoji: str, caption: str):
EMOJI_SIZE = 600
@ -117,9 +116,12 @@ def create_modern_meme_from_emoji(emoji: str, caption: str):
return create_modern_meme(base, caption)
class WebcomicPanel():
PANEL_SIZE = 400
FONT = "Impact.ttf"
COLOR = ColorScheme.WHITE_WITH_BLACK_BORDER
'''
A panel in a webcomic.
'''
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):
pass
@ -127,10 +129,13 @@ class WebcomicPanel():
def create_image(self) -> Image:
return AnimatedImage.new((self.PANEL_SIZE,self.PANEL_SIZE))
def add_text_box(self, base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], align="") -> 'AnimatedImage':
def add_text_box(self, base : Image, caption : str, region_size : 'tuple[int, int]', coordinates : 'tuple[int, int]', align="") -> 'AnimatedImage':
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):
'''
Panel with one character talking.
'''
def __init__(self, emoji, caption, words_in_background):
self.emoji = emoji
self.caption = caption
@ -156,6 +161,9 @@ class OneCharacterWebcomicPanel(WebcomicPanel):
return add_border_to_image(base)
class TwoCharacterWebcomicPanel(WebcomicPanel):
'''
Panel with two characters talking.
'''
def __init__(self, left_emoji, left_caption, right_emoji, right_caption):
self.left_emoji = left_emoji
self.left_caption = left_caption
@ -205,6 +213,9 @@ class TwoCharacterWebcomicPanel(WebcomicPanel):
return add_border_to_image(base)
class TitleCardWebcomicPanel(WebcomicPanel):
'''
A caption in a webcomic.
'''
def __init__(self, caption):
self.caption = caption
@ -222,13 +233,36 @@ def create_webcomic(layout : 'list[WebcomicPanel]'):
image = AnimatedImage.new((total_image_x_size, total_image_y_size))
for i in range(len(layout)):
print(i)
panel = layout[i]
x = i%2
y = math.floor(i/2)
image = image.paste(panel.create_image(), (x*assumed_panel_x_size, y*assumed_panel_y_size))
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)
if caption == "":
return base
if init_font_size == 0:
@ -244,6 +278,7 @@ def add_text_box(base : Image, caption : str, region_size : tuple[int, int], coo
fill_color = (255,255,255)
stroke = (0,0,0)
stroke_size = 3
caption = caption.upper()
line_image = ImageText((region_x_size+stroke_size, region_y_size))
place = "left"
if "ch" in align:
@ -285,7 +320,12 @@ def add_text_box(base : Image, caption : str, region_size : tuple[int, int], coo
else:
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)
print(font)
if caption == "":
return
region_x_size, region_y_size = region_size
@ -294,6 +334,9 @@ def add_text(base : Image, caption : str, region_size : tuple[int, int], coordin
return base.paste(AnimatedImage.from_image(line_image.image), coordinates)
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
WATERMARK_HEIGHT = int(0.05 * image.height)
image_size_x, image_size_y = image.size
@ -312,7 +355,11 @@ def add_watermark(image : Image, name_of_other_creator):
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)
image_size_x, image_size_y = to_paste.size
@ -331,6 +378,9 @@ def center_and_paste(base : Image, to_paste : Image, coordinates: tuple[int, int
return base.paste(to_paste, (x, y))
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
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))
@ -339,6 +389,9 @@ def add_border_to_image(image : Image, thickness : int = 5):
return outside_image
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
should_flip = False
if '!' in emoji_name:
@ -346,7 +399,7 @@ def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage':
should_flip = True
if (exists(get_real_filename(f"emoji_cache/{cleaned_emoji_name}.webp"))):
image = AnimatedImage.from_image(Image.open(f"emoji_cache/{cleaned_emoji_name}.webp"))
image = AnimatedImage.from_image(Image.open(get_real_filename(f"emoji_cache/{cleaned_emoji_name}.webp")))
else:
image = get_image_file_from_url(f"https://www.rdrama.net/e/{cleaned_emoji_name}.webp")
@ -356,6 +409,9 @@ def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage':
return image
def get_image_file_from_url(url) -> 'AnimatedImage':
'''
Gets the image at the given address.
'''
try:
r = requests.get(url)
image_file = io.BytesIO(r.content)
@ -363,7 +419,11 @@ def get_image_file_from_url(url) -> 'AnimatedImage':
return AnimatedImage.from_image(im)
except:
print(f"ERROR GETTING FILE FROM {url}")
def parse_caption_file(filename):
'''
Opens a file of strings and returns them.
'''
if not exists(filename):
return []
to_return = []
@ -372,53 +432,84 @@ def parse_caption_file(filename):
to_return.append(id.strip())
return to_return
watermark_captions = parse_caption_file(get_real_filename(CAPTION_FILENAME))
class AnimatedImage():
def __init__(self, frames : list[Image.Image]):
self.frames = frames
'''
Basically a list of images, meant to be animated.
'''
def __init__(self, frames : 'list[Image.Image]'):
if len(frames) > 100:
self.frames = frames[0:100]
else:
self.frames = frames
@property
def size(self) -> Tuple[int, int]:
return self.frames[0].size
def size(self) -> 'Tuple[int, int]':
'''
Size of the image.
'''
return self.frames[0].size
@property
def height(self) -> int:
'''
Height of the image.
'''
return self.frames[0].size[1]
@property
def width(self) -> int:
'''
Width of the image.
'''
return self.frames[0].size[0]
def flip(self) -> 'AnimatedImage':
new_frames = []
for frame in self.frames:
#frame.show()
new_frame = ImageOps.mirror(frame)
#new_frame.show()
new_frames.append(new_frame)
return AnimatedImage(new_frames)
'''
Flips image horizontally.
'''
return self.perform_transform_for_all_images(lambda image : ImageOps.mirror(image))
def save(self, filename) -> None:
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)
def get_binary(self) -> bytes:
'''
Converts the image to binary.
'''
output = io.BytesIO()
self.frames[0].save(output, format="webp", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
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':
'''
Converts a run of the mill PIL image to an AnimatedImage.
'''
frames = []
for image_frame in ImageSequence.Iterator(image):
frames.append(image_frame.copy())
return AnimatedImage(frames)
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_other_frames = len(other.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]
other_frame = other.frames[i%num_other_frames]
@ -431,14 +522,35 @@ class AnimatedImage():
new_frames.append(new_frame)
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)
return AnimatedImage.from_image(base)
def fit(self, region : Tuple[int, int]) -> 'AnimatedImage':
def fit(self, region : 'Tuple[int, int]') -> '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.
'''
return self.perform_transform_for_all_images(lambda image : ImageOps.contain(image, region))
def stretch(self, region : 'Tuple[int, int]') -> 'AnimatedImage':
'''
Resizes the image such that the final image has the exact dimensions given by region
'''
return self.perform_transform_for_all_images(lambda image : image.resize(region, Image.ANTIALIAS))
def stretch_maintain_aspect_ratio(self, size : int) -> 'AnimatedImage':
aspect_ratio = self.height / self.width
ideal_height = int(size * aspect_ratio)
return self.stretch((size, ideal_height))
def perform_transform_for_all_images(self, transform : 'Callable[[Image.Image], Image.Image]') -> 'AnimatedImage':
new_frames = []
for frame in self.frames:
new_frames.append(ImageOps.contain(frame, region))
new_frames.append(transform(frame))
return AnimatedImage(new_frames)
def prime_decomposition(a):
@ -479,6 +591,9 @@ def get_leftover_primes(a_, b_):
leftover_primes.append(i)
return leftover_primes
watermark_captions = parse_caption_file(get_real_filename(CAPTION_FILENAME))
# shooting = get_emoji_from_rdrama("!marseyshooting")
# shooting2 = get_emoji_from_rdrama("marseydeterminedgun")
@ -498,7 +613,7 @@ def get_leftover_primes(a_, b_):
# OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False)
# ]).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_classic_meme_from_url("https://rdrama.net/assets/images/rDrama/sidebar/98.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)").save("bruh.webp")
#create_modern_meme_from_url("https://cdn.discordapp.com/attachments/1007776259910152292/1007833711540174885/unknown.png", "and then heymoon says the imposter is among us").save("modern_image.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_classic_meme_from_emoji("marseycock", "I WANT TO PUT MY DICK", "INSIDE OF MARSEY").save("classic_with_emoji.webp")
#create_modern_meme_from_emoji("rdramajanny", "youll be sorry when i get my mop you BITCH").save("modern_emoji.webp")

View File

@ -0,0 +1,47 @@
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()
for comment in rdrama.get_comments(user = "automeme", sort = "top", t="day")['data']:
if "Darrell Brooks" not in comment['body'] and "twitter" not in comment['body']:
best = comment
break
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])

View File

@ -0,0 +1,98 @@
from io import BytesIO
import re
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()
TWEET_LENGTH = 280
URL_LENGTH = 23
HASHTAG_REGION = 23424977
IMAGE_URL_REGEX = r"https://rdrama\.net/images/([1234567890]*)\.webp"
TWEET_URL_REGEX = r"https://twitter\.com/([a-zA-Z]*)/status/([0-9]*)"
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()
def post_rdrama_tweet(post, api):
tweet_url = post['url']
tweet_text = get_tweet_text(post)
return api.update_status(tweet_text, attachment_url=tweet_url)
def post_rdrama_image(post, api):
animated_image = get_image_file_from_url(post['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
tweet_text = get_tweet_text(post)
return api.update_status(tweet_text, media_ids=[media_id])
def post_rdrama_basic_article(post, api):
tweet_text = get_tweet_text(post)
return api.update_status(tweet_text)
def hashtagify(string, api):
trends = api.get_place_trends(id=23424977)[0]['trends']
trend_words = []
for trend in trends:
name = trend['name'].lower()
if '#' in name:
trend_words.append(name[1:])
else:
for word in name.split(' '):
trend_words.append(word)
print(trend_words)
input = string
for hashtag in trend_words:
input = re.sub(f" {hashtag} ", f" #{hashtag} ", input)
return input
def get_tweet_text(post):
rdrama_url = post['permalink']
title = post['title']
text_space = TWEET_LENGTH - URL_LENGTH - 1
actual_title = re.sub(r":[^ ]*:", "", title) #remove marseys
actual_title = hashtagify(actual_title, api)
if (len(title) > text_space):
actual_title = actual_title[0:text_space-3] + "..."
else:
actual_title = actual_title
return f"{actual_title} {rdrama_url}"
def post_top_scoring_link(rdrama, api):
post = None
for current_post in rdrama.get_posts(sort='top', t='hour')['data']:
if not current_post['club']:
post = current_post
break
#post = rdrama.get_posts(sort='top', t='hour')['data'][0] #top scoring post
if re.match(TWEET_URL_REGEX, post['url']):
tweet = post_rdrama_tweet(post, api)
elif re.match(IMAGE_URL_REGEX, post['url']):
tweet = post_rdrama_image(post, api)
else:
tweet = post_rdrama_basic_article(post, api)
url = f"https://twitter.com/drama_meme/status/{tweet.id}"
rdrama.reply_to_post(post['id'], f"Nice post, bro! [I posted it to twitter]({url}).")
if __name__ == "__main__":
post_top_scoring_link(rdrama, api)