2.2.2
This commit is contained in:
@@ -1,6 +1,19 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.2.2] - 2026-03-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Required Text Feature for Config Validation:**
|
||||||
|
- Added `required_text` section to `post_tags` for tag-specific validation rules.
|
||||||
|
- Each tag can now have its own list of required text strings to validate posts.
|
||||||
|
- If a post with a tag doesn't contain any of the required text strings, a custom message is prepended to the bot's comment.
|
||||||
|
- Supports searching in title, body, or both (`search_in` field).
|
||||||
|
- Properly handles quoted phrases (e.g., `"Tiny Takeover"`) by stripping quotes before comparison.
|
||||||
|
- Allows multiple different validation rules within a single shared post_tag entry.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.2.1] - 2026-03-14
|
## [2.2.1] - 2026-03-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+91
-5
@@ -3,7 +3,7 @@ import praw
|
|||||||
from config import get_reddit, Config
|
from config import get_reddit, Config
|
||||||
from update_checker import start_update_checker
|
from update_checker import start_update_checker
|
||||||
|
|
||||||
BOT_VERSION = "2.2.1" # Change this for new releases
|
BOT_VERSION = "2.2.2" # Change this for new releases
|
||||||
BOT_NAME = "ModReplyBot" # Change this if bot name changes
|
BOT_NAME = "ModReplyBot" # Change this if bot name changes
|
||||||
|
|
||||||
import time
|
import time
|
||||||
@@ -47,8 +47,14 @@ class ModReplyBot:
|
|||||||
print(f"Chat message watcher error: {e}")
|
print(f"Chat message watcher error: {e}")
|
||||||
import time
|
import time
|
||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
def comment_only(self, submission, comment_text):
|
def comment_only(self, submission, comment_text, matched_tag=None):
|
||||||
try:
|
try:
|
||||||
|
# Check required text and prepend message if needed
|
||||||
|
if matched_tag and matched_tag in self.tag_required_text:
|
||||||
|
comment_text = self.check_required_text_and_prepend_message(
|
||||||
|
submission, comment_text, self.tag_required_text[matched_tag], matched_tag
|
||||||
|
)
|
||||||
|
|
||||||
comment = submission.reply(comment_text)
|
comment = submission.reply(comment_text)
|
||||||
comment.mod.distinguish(sticky=True)
|
comment.mod.distinguish(sticky=True)
|
||||||
print(f"Commented (no approval) on: {submission.id}")
|
print(f"Commented (no approval) on: {submission.id}")
|
||||||
@@ -62,6 +68,16 @@ class ModReplyBot:
|
|||||||
self.config_path = os.path.join(os.path.dirname(__file__), 'config', 'config.yaml')
|
self.config_path = os.path.join(os.path.dirname(__file__), 'config', 'config.yaml')
|
||||||
self.triggers = []
|
self.triggers = []
|
||||||
self.comments = []
|
self.comments = []
|
||||||
|
self.statuses = []
|
||||||
|
self.flair_ids = []
|
||||||
|
self.stickied = []
|
||||||
|
self.lock_post = []
|
||||||
|
self.lock_comment = []
|
||||||
|
self.trigger_required_text = []
|
||||||
|
self.tag_comments = {}
|
||||||
|
self.tag_statuses = {}
|
||||||
|
self.tag_flair_ids = {}
|
||||||
|
self.tag_required_text = {}
|
||||||
self.commented_posts = set()
|
self.commented_posts = set()
|
||||||
self.commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'commented_posts.txt')
|
self.commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'commented_posts.txt')
|
||||||
self.tagged_commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'tagged_commented_posts.txt')
|
self.tagged_commented_posts_file = os.path.join(os.path.dirname(__file__), 'DB', 'tagged_commented_posts.txt')
|
||||||
@@ -77,6 +93,64 @@ class ModReplyBot:
|
|||||||
if self.log_level == 'Debug' or (self.log_level == 'Default' and not debug_only):
|
if self.log_level == 'Debug' or (self.log_level == 'Default' and not debug_only):
|
||||||
print(message)
|
print(message)
|
||||||
|
|
||||||
|
def check_required_text_and_prepend_message(self, submission, comment_text, required_text_list, matched_tag=None):
|
||||||
|
"""Check if post contains required text strings, prepend message if not."""
|
||||||
|
if not required_text_list:
|
||||||
|
return comment_text
|
||||||
|
|
||||||
|
# Get post content based on search_in
|
||||||
|
title = submission.title.lower()
|
||||||
|
body = getattr(submission, 'selftext', '').lower()
|
||||||
|
title_and_body = title + ' ' + body
|
||||||
|
|
||||||
|
print(f"[DEBUG] Checking required_text for tag '{matched_tag}', title='{title}', body='{body}'")
|
||||||
|
|
||||||
|
for req_entry in required_text_list:
|
||||||
|
req_tag = req_entry.get('tag', '').strip().lower()
|
||||||
|
print(f"[DEBUG] Checking req_entry with tag '{req_tag}'")
|
||||||
|
if req_tag and matched_tag and req_tag != matched_tag.lower():
|
||||||
|
print(f"[DEBUG] Skipping req_entry, tag mismatch")
|
||||||
|
continue # This required_text is for a different tag
|
||||||
|
|
||||||
|
text_list = req_entry.get('text', '').split(',')
|
||||||
|
def normalize_req_text(item):
|
||||||
|
item = item.strip()
|
||||||
|
# Remove surrounding quotes (single or double) for exact phrases
|
||||||
|
if (item.startswith('"') and item.endswith('"')) or (item.startswith("'") and item.endswith("'")):
|
||||||
|
item = item[1:-1]
|
||||||
|
return item.strip().lower()
|
||||||
|
text_list = [normalize_req_text(t) for t in text_list if t.strip()]
|
||||||
|
print(f"[DEBUG] Required text_list: {text_list}")
|
||||||
|
|
||||||
|
search_in = req_entry.get('search_in', 'title_and_body').lower()
|
||||||
|
if search_in == 'title':
|
||||||
|
content = title
|
||||||
|
elif search_in == 'body':
|
||||||
|
content = body
|
||||||
|
else: # title_and_body
|
||||||
|
content = title_and_body
|
||||||
|
|
||||||
|
print(f"[DEBUG] Searching in '{search_in}', content='{content}'")
|
||||||
|
|
||||||
|
# Check if any required text is present
|
||||||
|
found = False
|
||||||
|
for req_text in text_list:
|
||||||
|
if req_text in content:
|
||||||
|
print(f"[DEBUG] Found required text '{req_text}' in content")
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
print(f"[DEBUG] Required text not found, prepending message")
|
||||||
|
# Prepend the message
|
||||||
|
message = req_entry.get('message', '').strip()
|
||||||
|
if message:
|
||||||
|
comment_text = message + '\n\n' + comment_text
|
||||||
|
else:
|
||||||
|
print(f"[DEBUG] Required text found, not prepending message")
|
||||||
|
|
||||||
|
return comment_text
|
||||||
|
|
||||||
def ensure_config_file(self):
|
def ensure_config_file(self):
|
||||||
import os
|
import os
|
||||||
if not os.path.exists(self.config_path):
|
if not os.path.exists(self.config_path):
|
||||||
@@ -130,9 +204,11 @@ class ModReplyBot:
|
|||||||
self.stickied = []
|
self.stickied = []
|
||||||
self.lock_post = []
|
self.lock_post = []
|
||||||
self.lock_comment = []
|
self.lock_comment = []
|
||||||
|
self.trigger_required_text = []
|
||||||
self.tag_comments = {}
|
self.tag_comments = {}
|
||||||
self.tag_statuses = {}
|
self.tag_statuses = {}
|
||||||
self.tag_flair_ids = {}
|
self.tag_flair_ids = {}
|
||||||
|
self.tag_required_text = {}
|
||||||
for entry in config.get('triggers', []):
|
for entry in config.get('triggers', []):
|
||||||
self.triggers.append(entry.get('trigger', '').strip())
|
self.triggers.append(entry.get('trigger', '').strip())
|
||||||
self.comments.append(entry.get('comment', '').strip())
|
self.comments.append(entry.get('comment', '').strip())
|
||||||
@@ -145,16 +221,19 @@ class ModReplyBot:
|
|||||||
lock_post_val = lock_post_val.lower() in ['true', '1', 'yes']
|
lock_post_val = lock_post_val.lower() in ['true', '1', 'yes']
|
||||||
self.lock_post.append(bool(lock_post_val))
|
self.lock_post.append(bool(lock_post_val))
|
||||||
self.lock_comment.append(bool(entry.get('lock_comment', False)))
|
self.lock_comment.append(bool(entry.get('lock_comment', False)))
|
||||||
|
self.trigger_required_text.append(entry.get('required_text', []))
|
||||||
for entry in config.get('post_tags', []):
|
for entry in config.get('post_tags', []):
|
||||||
tags_str = entry.get('tag', '').strip()
|
tags_str = entry.get('tag', '').strip()
|
||||||
comment = entry.get('comment', '').strip()
|
comment = entry.get('comment', '').strip()
|
||||||
status = entry.get('status', 'enabled').strip().lower()
|
status = entry.get('status', 'enabled').strip().lower()
|
||||||
flair_id = entry.get('flair_id', '').strip()
|
flair_id = entry.get('flair_id', '').strip()
|
||||||
|
required_text = entry.get('required_text', [])
|
||||||
tags = [t.strip().lower() for t in tags_str.split(',') if t.strip()]
|
tags = [t.strip().lower() for t in tags_str.split(',') if t.strip()]
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
self.tag_comments[tag] = comment
|
self.tag_comments[tag] = comment
|
||||||
self.tag_statuses[tag] = status
|
self.tag_statuses[tag] = status
|
||||||
self.tag_flair_ids[tag] = flair_id
|
self.tag_flair_ids[tag] = flair_id
|
||||||
|
self.tag_required_text[tag] = required_text
|
||||||
# Parse ignore_tags
|
# Parse ignore_tags
|
||||||
self.ignore_tags = set()
|
self.ignore_tags = set()
|
||||||
for entry in config.get('ignore_tags', []):
|
for entry in config.get('ignore_tags', []):
|
||||||
@@ -332,7 +411,7 @@ class ModReplyBot:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[BACKFILL] Error setting flair '{flair_id}' for post {submission.id}: {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}'")
|
print(f"[BACKFILL] Auto-commenting on post {submission.id} with tag '{matched_tag}'")
|
||||||
self.comment_only(submission, comment_text)
|
self.comment_only(submission, comment_text, matched_tag)
|
||||||
backfill_count += 1
|
backfill_count += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[BACKFILL] Error during backfill: {e}")
|
print(f"[BACKFILL] Error during backfill: {e}")
|
||||||
@@ -385,7 +464,7 @@ class ModReplyBot:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[TAG WATCH] Error setting flair '{flair_id}' for post {submission.id}: {e}")
|
print(f"[TAG WATCH] Error setting flair '{flair_id}' for post {submission.id}: {e}")
|
||||||
print(f"Auto-commenting on post {submission.id} with tag '{matched_tag}'")
|
print(f"Auto-commenting on post {submission.id} with tag '{matched_tag}'")
|
||||||
self.comment_only(submission, comment_text)
|
self.comment_only(submission, comment_text, matched_tag)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Tag post watcher error: {e}")
|
print(f"Tag post watcher error: {e}")
|
||||||
import time
|
import time
|
||||||
@@ -447,7 +526,7 @@ class ModReplyBot:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[MODQUEUE WATCH] Error setting flair '{flair_id}' for post {submission.id}: {e}")
|
print(f"[MODQUEUE WATCH] Error setting flair '{flair_id}' for post {submission.id}: {e}")
|
||||||
print(f"Auto-commenting on filtered/removed post {submission.id} with tag '{matched_tag}' from modqueue")
|
print(f"Auto-commenting on filtered/removed post {submission.id} with tag '{matched_tag}' from modqueue")
|
||||||
self.comment_only(submission, comment_text)
|
self.comment_only(submission, comment_text, matched_tag)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Modqueue watcher error: {e}")
|
print(f"Modqueue watcher error: {e}")
|
||||||
import time
|
import time
|
||||||
@@ -546,6 +625,13 @@ class ModReplyBot:
|
|||||||
print(f"[DEBUG] Locked post {submission.id}")
|
print(f"[DEBUG] Locked post {submission.id}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[DEBUG] Error locking post {submission.id}: {e}")
|
print(f"[DEBUG] Error locking post {submission.id}: {e}")
|
||||||
|
|
||||||
|
# Check required text for triggers
|
||||||
|
if trigger_idx < len(self.trigger_required_text):
|
||||||
|
comment_text = self.check_required_text_and_prepend_message(
|
||||||
|
submission, comment_text, self.trigger_required_text[trigger_idx], self.triggers[trigger_idx]
|
||||||
|
)
|
||||||
|
|
||||||
if status == 'enabled':
|
if status == 'enabled':
|
||||||
print(f"[DEBUG] Submission object: {submission}, ID: {submission.id}, Type: {type(submission)}")
|
print(f"[DEBUG] Submission object: {submission}, ID: {submission.id}, Type: {type(submission)}")
|
||||||
comment = submission.reply(comment_text)
|
comment = submission.reply(comment_text)
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
triggers:
|
||||||
|
- trigger: WC
|
||||||
|
status: enabled # enabled, disabled, log-only (Default: enabled)
|
||||||
|
flair_id: 924d06f8-4409-11eb-a08c-0ed3f1d75325
|
||||||
|
stickied: true
|
||||||
|
lock_comment: true
|
||||||
|
comment: |
|
||||||
|
__[We're looking for another moderator.](https://sh.reddit.com/r/MinecraftHelp/comments/1rktdzl/meta_were_still_looking_for_another_moderator/)__
|
||||||
|
|
||||||
|
|
||||||
|
Helpers, remember that ___all___ __top-level__ comments must be a genuine, good faith attempt to help OP. Comments breaking this [rule](https://sh.reddit.com/r/MinecraftHelp/wiki/rules/#wiki_5._commenting_rules) will be removed, and bans issued[.](https://reddit.com/u/{{author}}/)
|
||||||
|
|
||||||
|
__Links:__
|
||||||
|
|
||||||
|
[How to mark solved](https://www.reddit.com/r/MinecraftHelp/wiki/rules/#wiki_7._points_sytem_rules) || [How to delete your post](https://reddit.com/r/MinecraftHelp/wiki/faq#wiki_how_do_i_delete_a_post.2C_without_breaking_rule_7.3F) || [FAQ](https://reddit.com/r/MinecraftHelp/wiki/faq) || [Rules](https://reddit.com/r/MinecraftHelp/wiki/rules)
|
||||||
|
|
||||||
|
- trigger: Lock
|
||||||
|
status: enabled # enabled, disabled, log-only (Default: enabled)
|
||||||
|
stickied: true
|
||||||
|
lock_post: true
|
||||||
|
lock_comment: false
|
||||||
|
comment: |
|
||||||
|
# __POST LOCKED__
|
||||||
|
|
||||||
|
TESTING
|
||||||
|
|
||||||
|
- trigger: TEST2
|
||||||
|
status: enabled # enabled, disabled, log-only (Default: enabled)
|
||||||
|
comment: |
|
||||||
|
___TEST "___
|
||||||
|
|
||||||
|
post_tags:
|
||||||
|
- tag: Bedrock, Java, Launcher, Legacy
|
||||||
|
status: enabled # enabled, disabled, log-only (Default: enabled)
|
||||||
|
flair_id: 924d06f8-4409-11eb-a08c-0ed3f1d75325
|
||||||
|
comment: |
|
||||||
|
__[Click here if your post says "Sorry, this post was removed by Reddit’s filters"](https://sh.reddit.com/r/MinecraftHelp/wiki/faq/#wiki_why_are_my_posts.2Fcomments_not_showing_on_the_sub_straight_away.3F)[.](https://reddit.com/u/{{author}}/)__
|
||||||
|
|
||||||
|
__[We're looking for another moderator.](https://sh.reddit.com/r/MinecraftHelp/comments/1rktdzl/meta_were_still_looking_for_another_moderator/)__
|
||||||
|
|
||||||
|
|
||||||
|
Helpers, remember that ___all___ __top-level__ comments must be a genuine, good faith attempt to help OP. Comments breaking this [rule](https://sh.reddit.com/r/MinecraftHelp/wiki/rules/#wiki_5._commenting_rules) will be removed, and bans issued.
|
||||||
|
|
||||||
|
__Links:__
|
||||||
|
|
||||||
|
[How to mark solved](https://www.reddit.com/r/MinecraftHelp/wiki/rules/#wiki_7._points_sytem_rules) || [How to delete your post](https://reddit.com/r/MinecraftHelp/wiki/faq#wiki_how_do_i_delete_a_post.2C_without_breaking_rule_7.3F) || [FAQ](https://reddit.com/r/MinecraftHelp/wiki/faq) || [Rules](https://reddit.com/r/MinecraftHelp/wiki/rules)
|
||||||
|
|
||||||
|
required_text:
|
||||||
|
- text: Xbox, Nintendo, switch, Windows 10, PS4, PlayStation, Win10, PS5, Series X, Series S, android, iphone, ipad, ios, PC, computer, Gear VR, Fire, Samsung, Amazon Tablet, huawei, PSVR, surface pro, Google pixel, Chromebook, Chrome, x-box, Windows, win 10, play station, steam deck, steamdeck, steam-deck
|
||||||
|
tag: Bedrock
|
||||||
|
message: |
|
||||||
|
__It looks like you didn't mention your platform (e.g., Xbox, PlayStation, Switch, etc.). Please clarify for better help!__
|
||||||
|
search_in: title_and_body
|
||||||
|
- text: 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4,1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8, 1.8.8, 1.8.7, 1.8.6, 1.8.5, 1.8.4, 1.8.3, 1.8.2, 1.8.1, 1.8, 1.7.10, 1.7.9, 1.7.8, 1.7.7, 1.7.6, 1.7.5, 1.7.4, 1.7.2, 1.6.4, 1.6.2, 1.6.1, 1.5.2, 1.5.1, 1.5, 1.4.7, 1.4.6, 1.4.5, 1.4.4, 1.4.2, 1.3.2, 1.3.1, 1.2.5, 1.2.4, 1.2.3, 1.2.2, 1.2.1, 1.1, 1.0, a1.2.6, a1.2.5, a1.2.4_01, a1.2.3_04, a1.2.3_02, a1.2.3_01, a1.2.3,a .2.2b, a1.2.2a, a1.2.1_01, a1.2.1, a1.2.0_02, a1.2.0_01, a1.2.0, a1.1.2_01, a1.1.2, a1.1.0, a1.0.17_04, a1.0.17_02, a1.0.16, a1.0.15, a1.0.14, a1.0.11, a1.0.5_01, a1.0.4, inf-20100618, c0.30_01c, c0.0.13a, c0.0.13a_03, c0.0.11a, rd-161348, rd-160052, rd-20090515, rd-132328, rd-132211, b1.8.1, b1.8, b1.7.3, b1.7.2, b1.7, b1.6.6, b1.6.5, b1.6.4, b1.6.3, b1.6.2, b1.6.1, b1.6, b1.6-tb3, b1.5_01, b1.5, b1.4_01, b1.4, b1.3_01, b1.3b, b1.2_02, b1.2_01, b1.2, b1.1_02, b1.1_01, b1.0.2, b1.0_01, b1.0, "beta 1", "alpha 1", "alpha v1", 20W45A, 20W46A, 20W48A, 20W49A, 20W51A, 21W05A, 21W05B, 21W06A, 21W07A, 21W08A, 21W08B, 21W10A, 21W11A, 21W13A, 21W14A, 21W15A, 21W16A, 21W17A, 21W18A, 21w19a, 21W20A, 25w02a, 25w09a, 25w09b, pre-classic, classic, indev, infdev, "april fool", 2.0, 15w14a, "love and hugs", 1.RV-Pre1, "trendy update", "3d shareware", "20w14", 22w13oneBlockAtATime, "one block at a time", "23w13a_or_b", "vote update", 24w14potato, "potato update", 25w14craftmine, "craftmine update", "combat test", "multiplayer test", "survival test", "halloween update", "adventure update", "pretty scary update", "redstone update", "horse update", "changed the world", "bountiful update", "combat update", "frostburn update", "exploration update", "world of color", "update aquatic", "aquatic update", "pillage update", "village and pillage", "village & pillage", "buzzy bees", "nether update", "cliffs update", "wild update", "tales update", "trails update", "bats and pots", "armored paws", "tricky trials", "bundles of bravery", "garden awakens", "spring drop", "spring to life", "summer drop", "Chase the Skies", "fall drop", "Copper Age", "Mounts of Mayhem", "Tiny Takeover"
|
||||||
|
tag: Java
|
||||||
|
message: |
|
||||||
|
__It looks like you didn't mention your version (e.g., 26.1, 1.21.11, 1.12.2 etc.). Please clarify for better help!__
|
||||||
|
search_in: title_and_body
|
||||||
|
- text: Java, Bedrock, Dungeons, Legends, Account
|
||||||
|
tag: Launcher
|
||||||
|
message: |
|
||||||
|
__It looks like you didn't mention the game you're trying to play (e.g., Bedrock, Java etc.). Please clarify for better help!__
|
||||||
|
search_in: title_and_body
|
||||||
|
|
||||||
|
ignore_tags:
|
||||||
|
- tag: PSA
|
||||||
Reference in New Issue
Block a user