def __init__(self, plugin_args): self.generic_hooks = self.HookContainer(self.call_plugin_func) self.enabled_wiki_hooks = self.HookContainer(self.call_plugin_func) self.enabled_wikis = [] self.wiki_pages_callbacks = {} self.plugin_args = plugin_args.copy() self.logger = botlog(self.__repr__())
import time import configparser from oslo_concurrency.watchdog import watch from database.db import db_data from modbot import hook from modbot.commands import add_inbox_command, add_report_command, execute_inbox_command, execute_report_command # meh method of getting the callback list after loading, but works for now from modbot.hook import callbacks, plugins_with_wikis from modbot.hook import callback_type from modbot import utils from modbot.log import botlog from modbot.moderated_sub import DispatchAll, DispatchSubreddit from modbot.reddit_wrapper import get_moderated_subs, get_subreddit, start_tick, get_submission, watch_all, get_user logger = botlog("plugin") DISPATCH_ANY = 0 # Generic key for dispatching items to all non-bound hooks class plugin_manager(): # Dictionary of items that can be passed to plugins def __init__(self, bot_inst, master_subreddit, path_list=None, bot_config={}, db_params={}): """ Class that manages plugins from a given list of paths. :param bot_inst: bot instance
from modbot import hook from modbot.log import botlog from modbot.utils import parse_wiki_content plugin_documentation = """ The plugin sends submissions or modlog items to given discord webhooks. Configurable parameters are: - modlog - sends modlog items to the webhook - submissions - sends new submissions to the webhook Example configuration: modlog = http://webhook1 submissions = http://webhook2 """ logger = botlog("webhook_plugin") # Store wiki configuration per subreddit wiki_config = {} replaces = {"@everyone": "@ everyone", "@here": "@ here"} class PluginCfg(): def __init__(self, config): if "modlog" in config: self.modlog = config["modlog"] if "submissions" in config: self.submissions = config["submissions"]
import prawcore from modbot import utils from modbot.log import botlog from modbot.storage import dsdict logger = botlog("wiki_page") EMPTY_WIKI = "" RECENT_EDIT_LIMIT = utils.timedata.SEC_IN_MIN * 2 # TODO: merge this class with reddit_wrapper.py/wiki class WatchedWiki(): class WikiChange(): """ Represents a changed wiki transaction """ def __init__(self, wiki): self.author = wiki.author self.content = wiki.content self.revision_date = wiki.revision_date self.recent_edit = False # Check if it's a recent edit if utils.utcnow() - self.revision_date < RECENT_EDIT_LIMIT: self.recent_edit = True DOC_BEGIN = " ###### <Documentation> (do not edit below)\n\n" DOC_END = "\n\n ###### <Documentation> (do not edit above)\n"
import enum import pathlib import inspect from modbot.log import botlog, loglevel callbacks = [] plugins_with_wikis = [] logger = botlog('hook', file_level=loglevel.INFO) hook_rights = {} # Map of non standard hook rights @enum.unique class subreddit_type(enum.Enum): MASTER_SUBREDDIT = 0 # References the master subreddit @enum.unique class callback_type(enum.Enum): SUB = 0 # Submission COM = 1 # Comment PER = 2 # Periodic ONL = 3 # On load ONS = 4 # On start CMD = 5 # message command REP = 6 # report command MLG = 7 # modlog event @enum.unique class permission(enum.IntEnum):
- If the new title is "AAA BBB CC DDD EEE FF", it will be transformed into "AAA BBB DDD EEE" - Assuming that there is an older submission with the title "EEE DDD BBB", the new submission will be reported because it has 3 common words with the old one - ignore_users - list of users to ignore Recommended configuration: [Setup] minimum_word_length = 3 minimum_nb_words = 5 min_overlap_percent = 50 ignore_users = ["AutoModerator"] """ MAX_AGE = timedata.SEC_IN_DAY * 7 # Maximum age to keep posts MAX_ACTION_TIME = timedata.SEC_IN_MIN # Maximum time to wait to take an action logger = botlog("repost_detector") # Store wiki configuration per subreddit wiki_config = {} class RepostCfg(): def __init__(self, config): self.ignore_users = [] self.min_overlap_percent = min( max(int(config["min_overlap_percent"]), 0), 100) self.minimum_nb_words = int(config["minimum_nb_words"]) self.minimum_word_length = int(config["minimum_word_length"]) if "ignore_users" in config: raw_users = ast.literal_eval(config["ignore_users"])
import os import json import collections import platform import shutil from modbot.log import botlog, loglevel from shutil import copyfile from oslo_concurrency import lockutils logger = botlog("storage.log", console_level=loglevel.ERROR, file_level=loglevel.ERROR) DS_LOC = "storage_data/" dsdict_cache = {} def get_stored_dict(parent, name): path = "%s/%s" % (parent, name) if path not in dsdict_cache: dsdict_cache[path] = dsdict(parent, name) return dsdict_cache[path] class dstype(): def __init__(self, parent, name): if not name.endswith(".json"): name = name + ".json"
from modbot import hook from modbot import utils from modbot.log import botlog start_date = utils.date() logger = botlog("audit") wiki = hook.register_wiki_page( wiki_page="bot_startup", description="Marks when the bot has started up (always enabled)", default_enabled=True, subreddits=[hook.subreddit_type.MASTER_SUBREDDIT]) @hook.on_start(wiki=wiki) def mark_startup(wiki_pages): page = wiki_pages["bot_startup"][0] page.update_content() new_content = page.content.split("\n") new_content.insert( 0, "Bot startup: %s; Plugin startup: %s\n" % (start_date, utils.date())) logger.info("Bot startup: %s; Plugin startup: %s\n" % (start_date, utils.date())) # Only upload the last 100 startups page.set_content("\n".join(new_content[:100]))
This plugin checks if a link post has been posted with a different title than the original one. If the difference is larger than a given limit, the post is reported. Configurable parameters are: - minimum_overlap_percent - the post title and the article title must overlap at least the given amount, else it's reported (value should be given between 0 and 100) - domains - list of domains that should be checked - ignore_users - list of users to ignore Example configuration: minimum_overlap_percent = 60 domains = ["google.com", "blabla.co.uk"] ignore_users = ["AutoModerator"] """ MAX_ACTION_TIME = timedata.SEC_IN_MIN # Maximum time to wait to take an action logger = botlog("changed_title") # Store wiki configuration per subreddit wiki_config = {} class PluginCfg(): def __init__(self, config): self.domains = [] self.ignore_users = [] # Mark that a configuration is valid self.valid = False if "minimum_overlap_percent" not in config: return
import praw import prawcore import time import requests import json from modbot.log import botlog, loglevel from modbot.utils import utcnow, timedata praw_credentials = None praw_user_agent = None praw_inst = {} # Dictionary of praw sessions logger = botlog("redditinput", console_level=loglevel.DEBUG) audit = botlog("audit", console_level=loglevel.DEBUG) class Thing(): """ Thing class to mock reddit thing """ def __init__(self, id): self.id = id def set_praw_opts(credentials, user_agent): """ Set authentication options """ global praw_credentials global praw_user_agent
# Log all exceptions import sys import traceback from modbot.log import botlog logger = botlog("exception") def log_exception(type, value, tb): trace = traceback.format_list(traceback.extract_tb(tb)) logger.error(value.args[0] + "\n" + "".join(trace)) original_hook(type, value, tb) # Save the original hook and add a custom one original_hook = sys.excepthook sys.excepthook = log_exception
import logging import copy from modbot import utils from modbot.log import botlog from modbot.wiki_page import WatchedWiki from modbot.hook import callback_type from modbot.storage import get_stored_dict from modbot.utils import BotThread, cron_next logger = botlog("mod_sub") class DispatchAll(): class HookContainer(): def __init__(self, to_call): self.callbacks_peri = [] self.callbacks_subs = [] self.callbacks_coms = [] self.callbacks_mlog = [] self.callbacks_onstart = [] self.callbacks = [] self.to_call = to_call self.extra_args = {} def add_hook(self, func, last_exec=utils.utcnow()): # Create an unique copy for this container func = copy.copy(func) if func.ctype == callback_type.SUB: self.callbacks_subs.append(func) elif func.ctype == callback_type.COM:
from collections import OrderedDict from modbot import hook from modbot.log import botlog from modbot.utils import parse_wiki_content from modbot.reddit_wrapper import get_subreddit from modbot.utils_images import get_picture, gen_fname plugin_documentation = """ Change sidebar pictures each day. Configurable parameters are: Example configuration: """ logger = botlog("change_sidebar") # Store wiki configuration per subreddit wiki_config = {} START_MARKER = "[](/begin-pics)" END_MARKER = "[](/end-pics)" class PluginCfg(): def __init__(self, config): self.items = OrderedDict() for item in config: self.items[item] = config[item]
Example configuration: [some_channel] id = 123456 message = got a post by some channel report = reports are optional [some_other_channel] id = ABCDEFG message = blah [another_channel] id = ABCDEFG delete = True """ logger = botlog("yt") # Store wiki configuration per subreddit wiki_config = {} # Taken from: https://stackoverflow.com/questions/19377262/regex-for-youtube-url yt_validator = re.compile( r"^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$" ) class PluginCfg(): @dataclass class YTConfig: """Keeps config for a youtube channel.""" ytid: str
import pathlib from modbot import hook from modbot.log import botlog from modbot.reddit_wrapper import get_moderator_users from modbot.utils import BotThread from modbot.storage import get_stored_dict inbox_cmd_list = {} report_cmd_list = {} raw_cmd_list = {} cmd_prefix = "/" logger = botlog("commands") class command(): def __repr__(self): return "%s - %s" % (self.name, self.doc.strip()) def __init__(self, func, name, documentation, requested_args, path): self.func = func self.name = name self.doc = documentation self.requested_args = requested_args self.path = path self.plugin_name = pathlib.Path(path).name.replace(".py", "") def add_inbox_command(plugin_func):
plugin_documentation = """ Sends a modmail message when a specific word is detected in a comment or submission anywhere on reddit. A word must be at least 4 letters long. Shorter expressions will be ignored. Configurable parameters are: - word_list - list of words or expressions to trigger messages on - ignore_users - list of users to ignore Example configuration: [Setup] word_list = ["bla bla", "asdfg", "123456"] ignore_users = ["yosemitesam", "bugsbunny] """ MIN_LEN = 4 logger = botlog("notify_on_word") # Store wiki configuration per subreddit wiki_config = {} class PluginCfg(): def __init__(self, config): word_list = ast.literal_eval(config["word_list"]) self.word_list = [] for word in word_list: if len(word) <= MIN_LEN: continue self.word_list.append(word)
import traceback import time import base36 import importlib from modbot.log import botlog, loglevel from modbot.utils import utcnow, timedata, BotThread, get_utcnow from modbot.storage import dsdict, get_stored_dict from modbot.input.rpc_server import create_server logger = botlog("reddit_wrapper", console_level=loglevel.DEBUG) audit = botlog("audit", console_level=loglevel.DEBUG) watch_dict = {} # maps watched subreddits to threads posted_things_body = get_stored_dict("all", "posted") backend = None all_data = None subreddit_cache = None wiki_storages = None sub_feeder = None com_feeder = None bot_signature = None inbox_thread = None last_inbox_update = None report_cmds = None cache_data = None modlog_hist = None last_moderator_subs_check = 0 moderator_subs_list = []