From 8fe3d1f0306c2004869563a878906bfc1006560c Mon Sep 17 00:00:00 2001 From: Slfhstd Date: Sat, 14 Mar 2026 22:54:59 +0000 Subject: [PATCH] 2.2.1 --- .gitignore | 1 + CHANGELOG.md | 19 +++++ __pycache__/update_checker.cpython-314.pyc | Bin 0 -> 5060 bytes docker-compose.yml | 4 +- modreplybot.py | 95 ++++++++++++++++++++- 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 __pycache__/update_checker.cpython-314.pyc diff --git a/.gitignore b/.gitignore index fcc3a3d..8d77223 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ config/* tests.py message.md +docker/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 371b6ea..913ab86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # Changelog +## [2.2.1] - 2026-03-14 + +### Added +- **Post Backfill on Startup:** + - Added `backfill_recent_posts()` to automatically scan and comment on posts from the last 24 hours when bot starts up. + - Prevents missed posts when bot is offline for extended periods. + - Only processes posts created in the last 24 hours to avoid overwhelming backfill. +- **Duplicate Comment Detection:** + - Bot now checks if the exact comment has already been posted on a post before commenting again. + - Prevents duplicate comments on the same post. + +### Changed +- Enhanced tag_post_watcher with better debug output and attempt tracking. +- Startup now prioritizes backfill thread for catching missed posts. + +### Fixed +- Fixed issue where new submissions stream wasn't being processed (skip_existing=True issue). +- Improved stream error handling with attempt counter and better exception messages. + ## [2.2.0] - 2026-03-12 ### Added diff --git a/__pycache__/update_checker.cpython-314.pyc b/__pycache__/update_checker.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95a5cc14dc07bf400b5b8edb61cdccf334643db7 GIT binary patch literal 5060 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 literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml index 681eb09..3cc1bfc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,6 @@ services: env_file: - .env volumes: - - ./config:/app/config - - ./DB:/app/DB + - ./docker/config:/app/config + - ./docker/DB:/app/DB restart: unless-stopped diff --git a/modreplybot.py b/modreplybot.py index 4ee877f..780bb4c 100644 --- a/modreplybot.py +++ b/modreplybot.py @@ -3,7 +3,7 @@ import praw from config import get_reddit, Config from update_checker import start_update_checker -BOT_VERSION = "2.2.0" # Change this for new releases +BOT_VERSION = "2.2.1" # Change this for new releases BOT_NAME = "ModReplyBot" # Change this if bot name changes import time @@ -254,11 +254,99 @@ class ModReplyBot: threading.Thread(target=mod_report_watcher, daemon=True).start() threading.Thread(target=self.chat_message_watcher, daemon=True).start() + def backfill_recent_posts(): + """Scan recent posts from last 24 hours that bot may have missed while offline.""" + print("[BACKFILL] Starting backfill of recent posts (last 24 hours)...") + import time + current_time = time.time() + one_day_ago = current_time - (24 * 3600) # 24 hours in seconds + backfill_count = 0 + + try: + # Scan new posts, stopping when we hit posts older than 24 hours + for submission in self.subreddit.new(limit=1000): + if submission.created_utc < one_day_ago: + print(f"[BACKFILL] Reached posts older than 24 hours, stopping backfill.") + break + + if submission.id in self.commented_posts: + continue # Already processed + + flair = (getattr(submission, 'link_flair_text', '') or '').strip().lower() + title = submission.title.strip() + import re + title_tags = re.findall(r'\[(.*?)\]', title) + title_tags_lower = [t.strip().lower() for t in title_tags] + + print(f"[BACKFILL] Checking post {submission.id}: title='{title}', flair='{flair}', title_tags={title_tags_lower}") + + # Ignore if any ignore_tag matches + ignore = False + if flair in self.ignore_tags: + ignore = True + else: + for tag in title_tags_lower: + if tag in self.ignore_tags: + ignore = True + break + + if ignore: + print(f"[BACKFILL] Ignoring post {submission.id} due to ignore tag.") + continue + + matched_tag = None + if flair in self.tag_comments: + matched_tag = flair + else: + for tag in title_tags_lower: + if tag in self.tag_comments: + matched_tag = tag + break + + if matched_tag: + status = self.tag_statuses.get(matched_tag, 'enabled') + comment_text = self.tag_comments[matched_tag] + flair_id = self.tag_flair_ids.get(matched_tag, '') + + # Check if bot has already commented this exact text on the post + try: + submission.comments.replace_more(limit=0) + bot_already_commented = False + for comment in submission.comments.list(): + if comment.author and comment.author.name == self.reddit.user.me().name: + if comment.body == comment_text: + bot_already_commented = True + print(f"[BACKFILL] Bot already posted this comment on {submission.id}, skipping.") + break + + if bot_already_commented: + self.save_commented_post(submission.id) + continue + except Exception as e: + print(f"[BACKFILL] Error checking existing comments on {submission.id}: {e}") + + if flair_id: + try: + submission.flair.select(flair_id) + print(f"[BACKFILL] Set flair '{flair_id}' for post {submission.id}") + except Exception as e: + print(f"[BACKFILL] Error setting flair '{flair_id}' for post {submission.id}: {e}") + print(f"[BACKFILL] Auto-commenting on post {submission.id} with tag '{matched_tag}'") + self.comment_only(submission, comment_text) + backfill_count += 1 + except Exception as e: + print(f"[BACKFILL] Error during backfill: {e}") + print(f"[BACKFILL] Backfill complete. Commented on {backfill_count} posts, switching to stream mode.") + def tag_post_watcher(): + print("[TAG WATCH] Starting tag post watcher thread...") + attempt = 0 while True: try: - # Check new submissions + attempt += 1 + print(f"[TAG WATCH] Attempt {attempt}: Waiting for new submissions...") for submission in self.subreddit.stream.submissions(skip_existing=True): + print(f"[TAG WATCH] GOT SUBMISSION: {submission.id}") flair = (getattr(submission, 'link_flair_text', '') or '').strip().lower() title = submission.title.strip() import re @@ -367,6 +455,9 @@ class ModReplyBot: threading.Thread(target=tag_post_watcher, daemon=True).start() threading.Thread(target=modqueue_watcher, daemon=True).start() + + # Start backfill thread first so it catches posts from when bot was offline + threading.Thread(target=backfill_recent_posts, daemon=True).start() # Keep main thread alive while True: