Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5496ae64b6 | |||
| 818df56222 | |||
| 1e22463db0 | |||
| 5dc2098581 |
+27
-18
@@ -1,27 +1,36 @@
|
|||||||
|
|
||||||
|
# Bot authentication and global settings
|
||||||
username = ""
|
username = ""
|
||||||
password = ""
|
password = ""
|
||||||
client_id = ""
|
client_id = ""
|
||||||
client_secret = ""
|
client_secret = ""
|
||||||
user_agent = "Flair Timer Comment Bot"
|
user_agent = "Flair Timer Comment Bot"
|
||||||
|
|
||||||
#Subreddits
|
# Subreddit to monitor
|
||||||
subreddit = "" # "INEEEEDIT" "Ofcoursethatsathing" "All"
|
subreddit = "" # e.g. "INEEEEDIT", "Ofcoursethatsathing", "All"
|
||||||
|
|
||||||
flair_text = "Waiting for OP" # Case Sensitive
|
# How often should the bot scan the subreddit for these posts, in seconds
|
||||||
|
interval = 30
|
||||||
|
|
||||||
interval = 30 # How often should the bot scan the subreddit for these posts, in seconds. Higher = slower/less accurate/save resources, lower = faster/more accurate/use more resources.
|
# Max posts to search (for performance)
|
||||||
|
searchlimit = 600
|
||||||
|
|
||||||
hours = 48 # How many hours must the flair been on the post to send the notification
|
# Multiple flair time configs
|
||||||
|
# Each entry can have: flair_text, hours, comment_message, lock_post, distinguish_sticky
|
||||||
searchlimit = 600 # Max: 1000, this should only be limited to save on resources. The bot sorts by new and if it isn't catching posts that are being changed to the flair simply because they are too old (say the 301st post on the subreddit is changed to the flair) then increase this limit.
|
flair_times = [
|
||||||
|
{
|
||||||
# Comment message to post on old posts
|
"flair_text": "Waiting for OP", # Case Sensitive
|
||||||
comment_message = ""
|
"hours": 48, # How many hours must the flair been on the post to send the notification
|
||||||
|
"comment_message": "This post has had the 'Waiting for OP' flair for 48 hours.",
|
||||||
# Whether the bot should lock the post after posting the comment (True/False)
|
"lock_post": False,
|
||||||
# Default is False to avoid accidental locking; set to True to enable locking.
|
"distinguish_sticky": False
|
||||||
lock_post = False
|
},
|
||||||
|
# Add more configs as needed
|
||||||
# Whether the distinguished comment should be stickied (True/False)
|
# {
|
||||||
# Some subreddits may require `True` to keep moderator comments visible.
|
# "flair_text": "Needs Info",
|
||||||
distinguish_sticky = False
|
# "hours": 24,
|
||||||
|
# "comment_message": "This post has had the 'Needs Info' flair for 24 hours.",
|
||||||
|
# "lock_post": True,
|
||||||
|
# "distinguish_sticky": True
|
||||||
|
# },
|
||||||
|
]
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# flairconfig.py
|
||||||
|
# This file defines the list of flair time configs for the bot.
|
||||||
|
# Edit this file to customize flair behaviors.
|
||||||
|
|
||||||
|
flair_times = [
|
||||||
|
{
|
||||||
|
"flair_text": "Waiting for OP",
|
||||||
|
"hours": 48,
|
||||||
|
"comment_message": "This post has had the 'Waiting for OP' flair for 48 hours.",
|
||||||
|
"lock_post": False,
|
||||||
|
"distinguish_sticky": False
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"flair_text": "Solved",
|
||||||
|
"hours": 0.01,
|
||||||
|
"comment_message": "This post has had the 'Waiting for OP' flair for 48 hours.",
|
||||||
|
"lock_post": False,
|
||||||
|
"distinguish_sticky": False
|
||||||
|
},
|
||||||
|
# Add more configs as needed
|
||||||
|
]
|
||||||
+7
-6
@@ -1,4 +1,4 @@
|
|||||||
# Example .env file for FlairTimerComment
|
# Example .env file for FlairTimerCommentBot
|
||||||
# Fill in your Reddit API and bot settings
|
# Fill in your Reddit API and bot settings
|
||||||
|
|
||||||
USERNAME=your_reddit_username
|
USERNAME=your_reddit_username
|
||||||
@@ -7,11 +7,12 @@ CLIENT_ID=your_client_id
|
|||||||
CLIENT_SECRET=your_client_secret
|
CLIENT_SECRET=your_client_secret
|
||||||
USER_AGENT=Flair Timer Comment Bot
|
USER_AGENT=Flair Timer Comment Bot
|
||||||
|
|
||||||
|
# Subreddit to monitor
|
||||||
SUBREDDIT=your_subreddit
|
SUBREDDIT=your_subreddit
|
||||||
FLAIR_TEXT=Waiting for OP
|
|
||||||
INTERVAL=30
|
INTERVAL=30
|
||||||
HOURS=48
|
|
||||||
SEARCHLIMIT=600
|
SEARCHLIMIT=600
|
||||||
COMMENT_MESSAGE=Your comment message here
|
|
||||||
LOCK_POST=False
|
# Multiple flair configs as JSON string
|
||||||
DISTINGUISH_STICKY=False
|
# Example:
|
||||||
|
# FLAIR_TIMES_JSON=[{"flair_text": "Waiting for OP", "hours": 48, "comment_message": "Waiting for OP for 48 hours.", "lock_post": false, "distinguish_sticky": false}, {"flair_text": "Needs Info", "hours": 24, "comment_message": "Needs Info for 24 hours.", "lock_post": true, "distinguish_sticky": true}]
|
||||||
|
FLAIR_TIMES_JSON=[{"flair_text": "Waiting for OP", "hours": 48, "comment_message": "Waiting for OP for 48 hours.", "lock_post": false, "distinguish_sticky": false}]
|
||||||
|
|||||||
+95
-38
@@ -10,8 +10,11 @@ import time
|
|||||||
def env_or_default(var, default):
|
def env_or_default(var, default):
|
||||||
return os.environ.get(var, default)
|
return os.environ.get(var, default)
|
||||||
|
|
||||||
|
|
||||||
# Create config/config.py from environment if missing or empty
|
# Create config/config.py from environment if missing or empty
|
||||||
default_config_path = os.path.join('config', 'config.py')
|
default_config_path = os.path.join('config', 'config.py')
|
||||||
|
|
||||||
|
|
||||||
def write_config_from_env():
|
def write_config_from_env():
|
||||||
os.makedirs('config', exist_ok=True)
|
os.makedirs('config', exist_ok=True)
|
||||||
with open(default_config_path, 'w') as f:
|
with open(default_config_path, 'w') as f:
|
||||||
@@ -23,16 +26,12 @@ def write_config_from_env():
|
|||||||
f'user_agent = "{env_or_default("USER_AGENT", "Flair Timer Comment Bot")}"\n'
|
f'user_agent = "{env_or_default("USER_AGENT", "Flair Timer Comment Bot")}"\n'
|
||||||
'\n'
|
'\n'
|
||||||
f'subreddit = "{env_or_default("SUBREDDIT", "")}"\n'
|
f'subreddit = "{env_or_default("SUBREDDIT", "")}"\n'
|
||||||
f'flair_text = "{env_or_default("FLAIR_TEXT", "Waiting for OP")}"\n'
|
|
||||||
f'interval = {env_or_default("INTERVAL", "30")}\n'
|
f'interval = {env_or_default("INTERVAL", "30")}\n'
|
||||||
f'hours = {env_or_default("HOURS", "48")}\n'
|
|
||||||
f'searchlimit = {env_or_default("SEARCHLIMIT", "600")}\n'
|
f'searchlimit = {env_or_default("SEARCHLIMIT", "600")}\n'
|
||||||
f'comment_message = "{env_or_default("COMMENT_MESSAGE", "")}"\n'
|
|
||||||
f'lock_post = {env_or_default("LOCK_POST", "False")}\n'
|
|
||||||
f'distinguish_sticky = {env_or_default("DISTINGUISH_STICKY", "False")}\n'
|
|
||||||
)
|
)
|
||||||
print(f"Configuration file auto-populated from environment variables at {default_config_path}.")
|
print(f"Configuration file auto-populated from environment variables at {default_config_path}.")
|
||||||
|
|
||||||
|
|
||||||
# Check if config file exists and is non-empty, else generate from env
|
# Check if config file exists and is non-empty, else generate from env
|
||||||
def config_needs_populating():
|
def config_needs_populating():
|
||||||
if not os.path.exists(default_config_path):
|
if not os.path.exists(default_config_path):
|
||||||
@@ -44,67 +43,125 @@ def config_needs_populating():
|
|||||||
except Exception:
|
except Exception:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
if config_needs_populating():
|
if config_needs_populating():
|
||||||
write_config_from_env()
|
write_config_from_env()
|
||||||
# Optionally exit after populating, or continue to run
|
|
||||||
|
|
||||||
|
|
||||||
|
# Import main config
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
# Create default flairconfig.py if missing
|
||||||
|
flair_config_path = os.path.join('config', 'flairconfig.py')
|
||||||
|
|
||||||
|
|
||||||
|
def write_default_flairconfig():
|
||||||
|
if not os.path.exists(flair_config_path):
|
||||||
|
os.makedirs(os.path.dirname(flair_config_path), exist_ok=True)
|
||||||
|
with open(flair_config_path, 'w') as f:
|
||||||
|
f.write('# flairconfig.py\n')
|
||||||
|
f.write('# This file defines the list of flair time configs for the bot.\n')
|
||||||
|
f.write('flair_times = [\n')
|
||||||
|
f.write(' {\n')
|
||||||
|
f.write(' "flair_text": "Waiting for OP",\n')
|
||||||
|
f.write(' "hours": 48,\n')
|
||||||
|
f.write(' "comment_message": "This post has had the \'Waiting for OP\' flair for 48 hours.",\n')
|
||||||
|
f.write(' "lock_post": False,\n')
|
||||||
|
f.write(' "distinguish_sticky": False\n')
|
||||||
|
f.write(' },\n')
|
||||||
|
f.write(']\n')
|
||||||
|
print(f"Default flairconfig.py created at {flair_config_path}.")
|
||||||
|
|
||||||
|
|
||||||
|
write_default_flairconfig()
|
||||||
|
|
||||||
|
|
||||||
|
# Load flair_times from flairconfig.py
|
||||||
|
import importlib.util
|
||||||
|
spec = importlib.util.spec_from_file_location("flairconfig", flair_config_path)
|
||||||
|
flairconfig = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(flairconfig)
|
||||||
|
flair_times = getattr(flairconfig, "flair_times", [])
|
||||||
|
|
||||||
|
|
||||||
def authentication():
|
def authentication():
|
||||||
print ("Authenticating...")
|
print("Authenticating...")
|
||||||
reddit = praw.Reddit(username = config.username,
|
reddit = praw.Reddit(
|
||||||
password = config.password,
|
username=config.username,
|
||||||
client_id = config.client_id,
|
password=config.password,
|
||||||
client_secret = config.client_secret,
|
client_id=config.client_id,
|
||||||
user_agent = config.user_agent)
|
client_secret=config.client_secret,
|
||||||
print ("Authenticated as {}.".format(reddit.user.me()))
|
user_agent=config.user_agent
|
||||||
|
)
|
||||||
|
print("Authenticated as {}.".format(reddit.user.me()))
|
||||||
return reddit
|
return reddit
|
||||||
|
|
||||||
def main(reddit, posts: dict):
|
|
||||||
while True:
|
|
||||||
for submission in reddit.subreddit(config.subreddit).new(limit=config.searchlimit):
|
|
||||||
if not submission.saved:
|
|
||||||
if submission.id not in posts.keys() and submission.link_flair_text == config.flair_text:
|
|
||||||
posts[submission.id] = time.time()
|
|
||||||
print(f"Post {submission} has been flaired {config.flair_text}")
|
|
||||||
if submission.id in posts.keys() and submission.link_flair_text != config.flair_text:
|
|
||||||
posts.pop(submission.id)
|
|
||||||
print(f"Post {submission} has been unflaired {config.flair_text}")
|
|
||||||
|
|
||||||
for submission in posts:
|
def main(reddit, all_posts: dict):
|
||||||
if time.time() > posts[submission] + (config.hours * 60 * 60):
|
# all_posts structure: {flair_text: {submission_id: timestamp}}
|
||||||
posts.pop(submission)
|
while True:
|
||||||
reddit.submission(submission).save()
|
for flair_cfg in flair_times:
|
||||||
# Optionally lock the post if configured
|
flair_text = flair_cfg["flair_text"]
|
||||||
if getattr(config, 'lock_post', False):
|
hours = flair_cfg["hours"]
|
||||||
|
comment_message = flair_cfg["comment_message"]
|
||||||
|
lock_post = flair_cfg.get("lock_post", False)
|
||||||
|
distinguish_sticky = flair_cfg.get("distinguish_sticky", False)
|
||||||
|
|
||||||
|
# Ensure posts dict for this flair
|
||||||
|
posts = all_posts.setdefault(flair_text, {})
|
||||||
|
|
||||||
|
for submission in reddit.subreddit(config.subreddit).new(limit=config.searchlimit):
|
||||||
|
if not submission.saved:
|
||||||
|
if submission.id not in posts and submission.link_flair_text == flair_text:
|
||||||
|
posts[submission.id] = time.time()
|
||||||
|
print(f"Post {submission} has been flaired {flair_text}")
|
||||||
|
if submission.id in posts and submission.link_flair_text != flair_text:
|
||||||
|
posts.pop(submission.id)
|
||||||
|
print(f"Post {submission} has been unflaired {flair_text}")
|
||||||
|
|
||||||
|
expired = []
|
||||||
|
for submission_id, flair_time in posts.items():
|
||||||
|
if time.time() > flair_time + (hours * 60 * 60):
|
||||||
|
expired.append(submission_id)
|
||||||
|
|
||||||
|
for submission_id in expired:
|
||||||
|
posts.pop(submission_id)
|
||||||
|
subm = reddit.submission(submission_id)
|
||||||
|
subm.save()
|
||||||
|
if lock_post:
|
||||||
try:
|
try:
|
||||||
reddit.submission(submission).mod.lock()
|
subm.mod.lock()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Could not lock submission: {e}")
|
print(f"Could not lock submission: {e}")
|
||||||
|
|
||||||
comment = reddit.submission(submission).reply(body=config.comment_message)
|
comment = subm.reply(body=comment_message)
|
||||||
try:
|
try:
|
||||||
sticky = getattr(config, 'distinguish_sticky', False)
|
if distinguish_sticky:
|
||||||
if sticky:
|
|
||||||
comment.mod.distinguish(how="yes", sticky=True)
|
comment.mod.distinguish(how="yes", sticky=True)
|
||||||
else:
|
else:
|
||||||
comment.mod.distinguish(how="yes")
|
comment.mod.distinguish(how="yes")
|
||||||
print(f"Distinguished comment (sticky={sticky})")
|
print(f"Distinguished comment (sticky={distinguish_sticky})")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Could not distinguish comment: {e}")
|
print(f"Could not distinguish comment: {e}")
|
||||||
print(f"Post {submission} has been flaired {config.flair_text} for {config.hours} hours, posted comment")
|
print(f"Post {submission_id} has been flaired {flair_text} for {hours} hours, posted comment")
|
||||||
break
|
|
||||||
|
|
||||||
save_posts(posts)
|
save_posts(all_posts)
|
||||||
time.sleep(config.interval)
|
time.sleep(config.interval)
|
||||||
|
|
||||||
|
|
||||||
def load_posts():
|
def load_posts():
|
||||||
if not os.path.exists("config/posts.json"):
|
if not os.path.exists("config/posts.json"):
|
||||||
with open("config/posts.json", "w+") as file:
|
with open("config/posts.json", "w+") as file:
|
||||||
json.dump({}, file)
|
json.dump({}, file)
|
||||||
with open("config/posts.json", "r+") as file:
|
with open("config/posts.json", "r+") as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
|
# Ensure structure: {flair_text: {submission_id: timestamp}}
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
return {}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def save_posts(data):
|
def save_posts(data):
|
||||||
with open('config/posts.json', 'w+') as file:
|
with open('config/posts.json', 'w+') as file:
|
||||||
json.dump(data, file)
|
json.dump(data, file)
|
||||||
@@ -113,6 +170,6 @@ def save_posts(data):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
posts = load_posts()
|
posts = load_posts()
|
||||||
main(reddit = authentication(), posts = posts)
|
main(reddit=authentication(), all_posts=posts)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user