Updates pending full V1 release

This commit is contained in:
2026-03-10 18:37:11 +00:00
parent 3e5a330929
commit 9719a9b5f9
7 changed files with 230 additions and 75 deletions
+102 -60
View File
@@ -4,7 +4,7 @@ from config import get_reddit, Config
import time
class ModBot:
class ModReplyBot:
def __init__(self):
import os
self.reddit = get_reddit()
@@ -16,6 +16,11 @@ class ModBot:
self.commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'commented_posts.txt')
self.ensure_config_file()
self.load_commented_posts()
self.log_level = Config.LOG_LEVEL
def log(self, message, debug_only=False):
if self.log_level == 'Debug' or (self.log_level == 'Default' and not debug_only):
print(message)
def ensure_config_file(self):
import os
@@ -36,6 +41,9 @@ class ModBot:
f.write(default_yaml)
def fetch_yaml_config(self):
# Track last error revision to prevent modmail spam
if not hasattr(self, '_last_config_error_revision'):
self._last_config_error_revision = None
import yaml
try:
wiki_page = Config.WIKI_PAGE
@@ -43,6 +51,8 @@ class ModBot:
wiki_content = wiki.content_md
self._wiki_revision_id = getattr(wiki, 'revision_id', None)
config = yaml.safe_load(wiki_content)
if not isinstance(config, dict) or 'triggers' not in config:
raise ValueError("Wiki config missing required 'triggers' key or is not a dict.")
self.triggers = []
self.comments = []
self.statuses = []
@@ -60,8 +70,31 @@ class ModBot:
for tag in tags:
self.tag_comments[tag] = comment
self.tag_statuses[tag] = status
# Reset error revision tracker on successful config
self._last_config_error_revision = None
return True
except Exception as e:
print(f"Error fetching YAML config from wiki: {e}")
self.log(f"Error fetching YAML config from wiki: {e}")
# Only send modmail if revision is new
revision = getattr(self, '_wiki_revision_id', None)
if revision != self._last_config_error_revision:
self.notify_mods_config_error(str(e))
self._last_config_error_revision = revision
return False
def notify_mods_config_error(self, error_message):
try:
subject = "ModReplyBot wiki config error"
body = f"The bot detected an error in the wiki config page and will not reload until it is fixed.\n\nError details: {error_message}\n\nPlease check the wiki config for formatting or missing information."
data = {
"subject": subject,
"text": body,
"to": f"/r/{Config.SUBREDDIT}",
}
self.reddit.post("api/compose/", data=data)
self.log("Sent modmail notification about wiki config error.")
except Exception as e:
self.log(f"Error sending modmail notification about wiki config error: {e}")
def load_commented_posts(self):
try:
@@ -80,105 +113,114 @@ class ModBot:
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}")
config_ok = self.fetch_yaml_config()
if not config_ok:
self.log("Bot startup aborted due to wiki config error.")
return
self.log(f"Triggers loaded: {self.triggers}")
self.log(f"Tag comments loaded: {self.tag_comments}")
self.log(f"Reddit user: {self.reddit.user.me()}")
self.log(f"Subreddit: {self.subreddit.display_name}")
except Exception as e:
print(f"Startup error: {e}")
self.log(f"Startup error: {e}")
return
def comment_watcher():
def mod_comment_watcher():
last_revision = None
while True:
try:
old_revision = last_revision
self.fetch_yaml_config()
config_ok = 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)
if config_ok:
self.log("Wiki config changed, reloading triggers and tag comments.")
self.notify_mods_config_change(new_revision)
else:
self.log("Wiki config error detected, not reloading bot.")
self.log_level = Config.LOG_LEVEL
last_revision = new_revision
for comment in self.subreddit.stream.comments(skip_existing=True):
self.handle_comment(comment)
if comment.author and comment.author in self.subreddit.moderator():
self.handle_comment(comment)
except Exception as e:
print(f"Comment watcher error: {e}")
# No sleep needed, stream blocks
print(f"Mod comment watcher error: {e}")
def submission_watcher():
seen_submissions = set()
def mod_report_watcher():
last_revision = None
while True:
try:
old_revision = last_revision
self.fetch_yaml_config()
config_ok = 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)
if config_ok:
self.log("Wiki config changed, reloading triggers and tag comments.")
self.notify_mods_config_change(new_revision)
else:
self.log("Wiki config error detected, not reloading bot.")
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.")
for submission in self.subreddit.mod.stream.reports():
self.handle_submission(submission)
except Exception as e:
print(f"Submission watcher error: {e}")
print(f"Mod report watcher error: {e}")
import time
time.sleep(5)
time.sleep(30)
threading.Thread(target=comment_watcher, daemon=True).start()
threading.Thread(target=submission_watcher, daemon=True).start()
threading.Thread(target=mod_comment_watcher, daemon=True).start()
threading.Thread(target=mod_report_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}")
# Respond to mod reports containing trigger phrases
print(f"New mod report detected: {submission.id} | Title: {submission.title}")
matched_trigger = None
matched_comment = None
matched_tag = None
matched_status = 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
matched_status = self.tag_statuses.get(tag_lower, 'enabled')
break
if matched_comment:
if submission.id in self.commented_posts:
print(f"Already auto-commented on post {submission.id}, skipping.")
return
# Check report reasons for triggers
if hasattr(submission, 'mod_reports') and submission.mod_reports:
for report_tuple in submission.mod_reports:
report_reason = report_tuple[0].strip().lower()
for idx, trigger in enumerate(self.triggers):
expected = f"!{trigger.lower()}"
if expected in report_reason:
matched_trigger = trigger
matched_comment = self.comments[idx]
matched_status = self.statuses[idx] if idx < len(self.statuses) else 'enabled'
break
if matched_trigger:
break
if matched_trigger:
# Only skip if this exact trigger was already actioned for this post
trigger_key = f"{submission.id}:{matched_trigger}"
if hasattr(self, 'triggered_posts'):
if trigger_key in self.triggered_posts:
print(f"Already actioned trigger [{matched_trigger}] on post {submission.id}, skipping.")
return
else:
self.triggered_posts = set()
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
if matched_status == 'enabled':
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)
print(f"Commented on mod report {submission.id} for trigger [{matched_trigger}] (auto)")
self.triggered_posts.add(trigger_key)
elif matched_status == 'log-only':
print(f"Log-only: Did not comment on post {submission.id} with tag [{matched_tag}] (auto)")
self.save_commented_post(submission.id)
print(f"Log-only: Did not comment on mod report {submission.id} for trigger [{matched_trigger}] (auto)")
self.triggered_posts.add(trigger_key)
elif matched_status == 'disabled':
print(f"Disabled: Did not comment/log for post {submission.id} with tag [{matched_tag}] (auto)")
print(f"Disabled: Did not comment/log for mod report {submission.id} for trigger [{matched_trigger}] (auto)")
else:
print(f"Unknown status '{matched_status}' for post {submission.id} with tag [{matched_tag}] (auto)")
print(f"Unknown status '{matched_status}' for mod report {submission.id} for trigger [{matched_trigger}] (auto)")
except Exception as e:
print(f"Error commenting on new post: {e}")
print(f"Error commenting on mod report: {e}")
else:
print(f"No matching tag found for post {submission.id}")
print(f"No matching trigger found in mod report for post {submission.id}")
def handle_comment(self, comment):
comment_body = comment.body.lower()
@@ -234,5 +276,5 @@ class ModBot:
print(f"Error sending modmail notification: {e}")
if __name__ == "__main__":
bot = ModBot()
bot = ModReplyBot()
bot.run()