Files
Modreplybot/modreplybot.py
T

211 lines
9.0 KiB
Python

import os
import praw
from config import get_reddit, Config
import time
class ModBot:
def __init__(self):
import os
self.reddit = get_reddit()
self.subreddit = self.reddit.subreddit(Config.SUBREDDIT)
self.config_path = os.path.join(os.path.dirname(__file__), 'config', 'config.yaml')
self.triggers = []
self.comments = []
self.commented_posts = set()
self.commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'commented_posts.txt')
self.ensure_config_file()
self.load_commented_posts()
def ensure_config_file(self):
import os
if not os.path.exists(self.config_path):
default_yaml = (
'triggers:\n'
' - trigger: help\n'
' comment: |\n'
' Thank you for your report!\n'
' This post is now approved.\n'
' - trigger: question\n'
' comment: |\n'
' This post has been approved.\n'
' Your question will be answered soon.\n'
)
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
with open(self.config_path, 'w', encoding='utf-8') as f:
f.write(default_yaml)
def fetch_yaml_config(self):
import yaml
try:
wiki_page = Config.WIKI_PAGE
wiki = self.subreddit.wiki[wiki_page]
wiki_content = wiki.content_md
self._wiki_revision_id = getattr(wiki, 'revision_id', None)
config = yaml.safe_load(wiki_content)
self.triggers = []
self.comments = []
self.tag_comments = {}
for entry in config.get('triggers', []):
self.triggers.append(entry.get('trigger', '').strip())
self.comments.append(entry.get('comment', '').strip())
for entry in config.get('post_tags', []):
tags_str = entry.get('tag', '').strip()
comment = entry.get('comment', '').strip()
tags = [t.strip().lower() for t in tags_str.split(',') if t.strip()]
for tag in tags:
self.tag_comments[tag] = comment
except Exception as e:
print(f"Error fetching YAML config from wiki: {e}")
def load_commented_posts(self):
try:
with open(self.commented_posts_file, 'r', encoding='utf-8') as f:
for line in f:
self.commented_posts.add(line.strip())
except FileNotFoundError:
pass
def save_commented_post(self, post_id):
self.commented_posts.add(post_id)
with open(self.commented_posts_file, 'a', encoding='utf-8') as f:
f.write(post_id + '\n')
def run(self):
import threading
print("ModReplyBot started. Watching for comments and new posts...")
try:
self.fetch_yaml_config()
print(f"Triggers loaded: {self.triggers}")
print(f"Tag comments loaded: {self.tag_comments}")
print(f"Reddit user: {self.reddit.user.me()}")
print(f"Subreddit: {self.subreddit.display_name}")
except Exception as e:
print(f"Startup error: {e}")
return
def comment_watcher():
last_revision = None
while True:
try:
old_revision = last_revision
self.fetch_yaml_config()
new_revision = getattr(self, '_wiki_revision_id', None)
if old_revision and new_revision and old_revision != new_revision:
print("Wiki config changed, reloading triggers and tag comments.")
self.notify_mods_config_change(new_revision)
last_revision = new_revision
for comment in self.subreddit.stream.comments(skip_existing=True):
self.handle_comment(comment)
except Exception as e:
print(f"Comment watcher error: {e}")
# No sleep needed, stream blocks
def submission_watcher():
seen_submissions = set()
last_revision = None
while True:
try:
old_revision = last_revision
self.fetch_yaml_config()
new_revision = getattr(self, '_wiki_revision_id', None)
if old_revision and new_revision and old_revision != new_revision:
print("Wiki config changed, reloading triggers and tag comments.")
self.notify_mods_config_change(new_revision)
last_revision = new_revision
new_submissions = list(self.subreddit.new(limit=10))
found_submission = False
for submission in new_submissions:
if submission.id not in seen_submissions:
found_submission = True
seen_submissions.add(submission.id)
self.handle_submission(submission)
if not found_submission:
print("No submissions detected in new() this cycle.")
except Exception as e:
print(f"Submission watcher error: {e}")
import time
time.sleep(5)
threading.Thread(target=comment_watcher, daemon=True).start()
threading.Thread(target=submission_watcher, daemon=True).start()
# Keep main thread alive
while True:
import time
time.sleep(60)
def handle_submission(self, submission):
# Respond to new posts based on tag(s) in title
import re
title = submission.title
print(f"New post detected: {submission.id} | Title: {title}")
tag_matches = re.findall(r"\[(.+?)\]", title)
print(f"Tags found in title: {tag_matches}")
matched_comment = None
matched_tag = None
for tag in tag_matches:
tag_lower = tag.strip().lower()
if tag_lower in self.tag_comments:
matched_comment = self.tag_comments[tag_lower]
matched_tag = tag_lower
break
if matched_comment:
if submission.id in self.commented_posts:
print(f"Already auto-commented on post {submission.id}, skipping.")
return
try:
footer = "^I ^am ^a ^bot ^and ^this ^comment ^was ^made ^automatically. ^Message ^the ^Mod ^team ^if ^I'm ^not ^working ^correctly."
comment_text = matched_comment.replace("{{author}}", submission.author.name if submission.author else "unknown") + "\n\n" + footer
comment = submission.reply(comment_text)
comment.mod.distinguish(sticky=True)
print(f"Commented on new post {submission.id} with tag [{matched_tag}] (auto)")
self.save_commented_post(submission.id)
except Exception as e:
print(f"Error commenting on new post: {e}")
else:
print(f"No matching tag found for post {submission.id}")
def handle_comment(self, comment):
comment_body = comment.body.lower()
for idx, trigger in enumerate(self.triggers):
expected = f"!{trigger.lower()}"
words = [w.strip() for w in comment_body.split()]
# Only respond if author is a moderator
if expected in words and comment.author and comment.author in self.subreddit.moderator():
try:
comment.mod.remove()
print(f"Removed triggering comment: {comment.id}")
except Exception as e:
print(f"Error removing comment: {e}")
submission = comment.submission
self.fetch_yaml_config()
self.approve_and_comment(submission, self.comments[idx])
break
def approve_and_comment(self, submission, comment_text):
try:
submission.mod.approve()
comment = submission.reply(comment_text)
comment.mod.distinguish(sticky=True)
print(f"Approved and commented on: {submission.id}")
except Exception as e:
print(f"Error approving/commenting: {e}")
def notify_mods_config_change(self, revision_id):
try:
subject = "ModReplyBot config wiki changed"
body = f"The config wiki page was updated (revision: {revision_id}).\n\nBot restarted and is running successfully."
data = {
"subject": subject,
"text": body,
"to": f"/r/{Config.SUBREDDIT}",
}
self.reddit.post("api/compose/", data=data)
print("Sent modmail notification about config change.")
except Exception as e:
print(f"Error sending modmail notification: {e}")
if __name__ == "__main__":
bot = ModBot()
bot.run()