From 6d2d04ea40ac46c84a3f8b3e80d9d4bd0b17cfa8 Mon Sep 17 00:00:00 2001 From: Slfhstd Date: Thu, 12 Mar 2026 22:56:57 +0000 Subject: [PATCH] 2.2.0 --- CHANGELOG.md | 21 ++++++++ DB/chat_wiki_requests.txt | 6 +++ DB/commented_posts.txt | 2 + Dockerfile | 1 + README.md | 7 +++ docker-compose.yml | 2 +- modreplybot.py | 24 +++++++++ update_checker.py | 100 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 update_checker.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a7b2b..371b6ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ + # Changelog +## [2.2.0] - 2026-03-12 + +### Added +- **Configurable Ignore Tags:** + - Added `ignore_tags` section to wiki config. Bot now ignores posts with matching tags in the title or flair (case-insensitive). + - Updated README.md and bot logic to support this feature. +- **Update Checker Integration:** + - Added `update_checker.py` module to check for bot updates and notify moderators via modmail. + - Update checker runs in a background thread and checks for updates hourly. +- **Bot Version & Name Constants:** + - Added `BOT_VERSION` and `BOT_NAME` constants at the top of `modreplybot.py` for easy version/name changes. + +### Changed +- Integrated update checker startup in main bot entrypoint. +- Improved documentation for ignore_tags and update checker features. + +### Fixed +- N/A + + ## [2.1.2] - 2026-03-10 ### Fixed diff --git a/DB/chat_wiki_requests.txt b/DB/chat_wiki_requests.txt index a7063f4..e1ed3d3 100644 --- a/DB/chat_wiki_requests.txt +++ b/DB/chat_wiki_requests.txt @@ -36,3 +36,9 @@ o846f5z 5judtbe 5jugfpj 5jv66nh +5jvi6wk +5jvji2r +o9wezfi +o9wf1xl +o9x1jh0 +o9x1lyh diff --git a/DB/commented_posts.txt b/DB/commented_posts.txt index 002d2c1..b362ac6 100644 --- a/DB/commented_posts.txt +++ b/DB/commented_posts.txt @@ -68,3 +68,5 @@ 1rqd9ds 1rqd966 1rqd9aw +1rs5sz0 +1rs5sxl diff --git a/Dockerfile b/Dockerfile index 77d3a37..1f50e0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,5 +5,6 @@ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY config.py . COPY modreplybot.py . +COPY update_checker.py . ENV PYTHONUNBUFFERED=1 CMD ["python", "modreplybot.py"] diff --git a/README.md b/README.md index 9bf1e5b..dd5decf 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,17 @@ post_tags: __[Click here if your post says "Sorry, this post was removed by Reddit’s filters"](...)__ status: enabled flair_id: 12345678-aaaa-bbbb-cccc-1234567890ab + +# New: Ignore tags +ignore_tags: + - tag: Off-Topic + - tag: Meme ``` - Triggers: Bot responds to mod comments and mod reports containing `!trigger` (e.g., `!help`) with the configured comment and actions. + - post_tags: Bot posts the comment automatically on new posts with matching tags in the title and can set flair. +- ignore_tags: Bot will ignore (not comment on) posts with these tags in the title. Tags are case-insensitive and match `[Tag]` in the post title or flair. - Status options: `enabled`, `log-only`, `disabled`. - Optional actions: `flair_id`, `stickied`, `lock_post`, `lock_comment`. diff --git a/docker-compose.yml b/docker-compose.yml index 8e0263d..681eb09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: modreplybot: - image: slfhstd.uk/slfhstd/modreplybot:latest + image: slfhstd.uk/slfhstd/modreplybot:dev container_name: modreplybot env_file: - .env diff --git a/modreplybot.py b/modreplybot.py index 928e34e..4ee877f 100644 --- a/modreplybot.py +++ b/modreplybot.py @@ -1,6 +1,10 @@ import os 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_NAME = "ModReplyBot" # Change this if bot name changes import time @@ -151,6 +155,12 @@ class ModReplyBot: self.tag_comments[tag] = comment self.tag_statuses[tag] = status self.tag_flair_ids[tag] = flair_id + # Parse ignore_tags + self.ignore_tags = set() + for entry in config.get('ignore_tags', []): + tag_val = entry.get('tag', '').strip().lower() if isinstance(entry, dict) else str(entry).strip().lower() + if tag_val: + self.ignore_tags.add(tag_val) self._last_config_error_revision = None return True @@ -255,6 +265,18 @@ class ModReplyBot: title_tags = re.findall(r'\[(.*?)\]', title) title_tags_lower = [t.strip().lower() for t in title_tags] print(f"[TAG WATCH] Post {submission.id}: title='{title}', flair='{flair}', title_tags={title_tags_lower}") + # Ignore if any ignore_tag matches flair or title tag + 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"[TAG WATCH] Ignoring post {submission.id} due to ignore tag.") + continue matched_tag = None if flair in self.tag_comments: matched_tag = flair @@ -484,4 +506,6 @@ class ModReplyBot: if __name__ == "__main__": bot = ModReplyBot() + # Start update checker thread + start_update_checker(bot.reddit, Config.SUBREDDIT, BOT_NAME, BOT_VERSION) bot.run() diff --git a/update_checker.py b/update_checker.py new file mode 100644 index 0000000..41d3604 --- /dev/null +++ b/update_checker.py @@ -0,0 +1,100 @@ +import requests +import threading +import time +import os +from datetime import datetime + +UPDATE_CHECK_INTERVAL = 3600 # Check every hour +UPDATE_COOLDOWN = 86400 # Don't send mail more than once per 24 hours +LAST_UPDATE_FILE = os.path.join(os.path.dirname(__file__), 'DB', '.last_update_check.txt') + + +def get_latest_version(bot_name): + """Fetch latest version info from update server.""" + try: + response = requests.get(f'https://updts.slfhstd.uk/api/version/{bot_name}', timeout=10) + if response.status_code == 200: + return response.json() + except Exception as e: + print(f"[UPDATE_CHECKER] Error fetching version: {e}") + return None + + +def send_update_modmail(reddit, subreddit_name, bot_name, current_version, available_version, changelog_url): + """Send modmail to subreddit modteam about available update.""" + try: + subject = f"🤖 {bot_name} Update Available (v{available_version})" + message = f"""Hello, + +An update is available for {bot_name}! + +**Current Version:** {current_version} +**Available Version:** {available_version} + +Changelog: {changelog_url} + +Please visit the update server for installation instructions. + +--- +This is an automated message from the Update Checker.""" + data = { + "subject": subject, + "text": message, + "to": f"/r/{subreddit_name}", + } + reddit.post("api/compose/", data=data) + print(f"[UPDATE_CHECKER] Sent update notification to r/{subreddit_name}") + return True + except Exception as e: + print(f"[UPDATE_CHECKER] Error sending modmail: {e}") + return False + + +def should_send_update_mail(): + """Check if enough time has passed since last update mail.""" + if not os.path.exists(LAST_UPDATE_FILE): + return True + try: + with open(LAST_UPDATE_FILE, 'r') as f: + last_check = float(f.read().strip()) + return (time.time() - last_check) >= UPDATE_COOLDOWN + except: + return True + + +def mark_update_mailed(): + """Record when update mail was sent.""" + os.makedirs(os.path.dirname(LAST_UPDATE_FILE), exist_ok=True) + with open(LAST_UPDATE_FILE, 'w') as f: + f.write(str(time.time())) + + +def update_checker_thread(reddit, subreddit_name, bot_name, current_version): + """Background thread that checks for updates periodically.""" + print(f"[UPDATE_CHECKER] Started for {bot_name} v{current_version}") + while True: + try: + latest = get_latest_version(bot_name) + if latest: + available_version = latest.get('version') + changelog_url = latest.get('changelog_url', '') + if available_version and available_version != current_version: + if should_send_update_mail(): + sent = send_update_modmail( + reddit, subreddit_name, bot_name, current_version, available_version, changelog_url + ) + if sent: + mark_update_mailed() + else: + print(f"[UPDATE_CHECKER] No version info received.") + except Exception as e: + print(f"[UPDATE_CHECKER] Error in update checker thread: {e}") + time.sleep(UPDATE_CHECK_INTERVAL) + + +def start_update_checker(reddit, subreddit_name, bot_name, current_version): + threading.Thread( + target=update_checker_thread, + args=(reddit, subreddit_name, bot_name, current_version), + daemon=True + ).start()