From ad007972310caa7ec50699fe13d50e15d573823c Mon Sep 17 00:00:00 2001 From: Slfhstd Date: Sun, 8 Mar 2026 23:27:43 +0000 Subject: [PATCH] Updates to use wiki page as config and auto-detec these changes. Bot can also auto-comment on new posts if s pecific [Tag] is detected. --- DB/commented_posts.txt | 10 +++ docker-compose.yml | 3 +- modreplybot.py | 140 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 DB/commented_posts.txt diff --git a/DB/commented_posts.txt b/DB/commented_posts.txt new file mode 100644 index 0000000..3f3f5e4 --- /dev/null +++ b/DB/commented_posts.txt @@ -0,0 +1,10 @@ +1roi8zs +1roi5cl +1roi245 +1roi088 +1rlvzus +1rlvufz +1rlvqyo +1rlvfof +1rlpiwm +1rlpivb diff --git a/docker-compose.yml b/docker-compose.yml index 4b75287..9976646 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,5 +5,6 @@ services: env_file: - .env volumes: - - ./config:/app/config + - ./config:/app/config + - ./DB:/app/DB restart: unless-stopped diff --git a/modreplybot.py b/modreplybot.py index d030661..6361651 100644 --- a/modreplybot.py +++ b/modreplybot.py @@ -1,3 +1,4 @@ +import os import praw from config import get_reddit, Config @@ -11,7 +12,10 @@ class ModBot: 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 @@ -34,47 +38,145 @@ class ModBot: def fetch_yaml_config(self): import yaml try: - with open(self.config_path, 'r', encoding='utf-8') as f: - config = yaml.safe_load(f) + 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: {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): - print("ModReplyBot started. Watching for comments...") + 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: - self.fetch_yaml_config() - for comment in self.subreddit.stream.comments(skip_existing=True): - self.handle_comment(comment) + 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"Main loop error: {e}") - time.sleep(5) # Poll every 5 seconds + 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()}" - if expected in comment_body: - # Remove the triggering comment + 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}") - # Approve the submission and post bot's comment submission = comment.submission self.fetch_yaml_config() self.approve_and_comment(submission, self.comments[idx]) @@ -89,6 +191,20 @@ class ModBot: 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()