diff --git a/extensions/hooks/example_hook.py b/extensions/hooks/example_hook.py
index aa4f049..983b76c 100644
--- a/extensions/hooks/example_hook.py
+++ b/extensions/hooks/example_hook.py
@@ -27,3 +27,21 @@ def example_pre_hook(context: Context, change: dict):
def example_post_hook(message: DiscordMessage, metadata: DiscordMessageMetadata, context: Context, change: dict):
print("Our Discord message looks as follows: ")
print(message)
+
+
+def verify_upload(context: Context, change: dict) -> bool:
+ return hasattr(context, "event") and context.event.startswith("upload")
+
+
+@pre_hook(priority=105, condition=verify_upload)
+def example_pre_hook_with_args(context: Context, change: dict):
+ print("I'm an upload! Also I'm using a fancy decorator!")
+
+
+def never_register_this_one(settings):
+ return False
+
+
+@pre_hook(priority=2, register=never_register_this_one)
+def example_pre_hook_that_will_never_register(context: Context, change: dict):
+ print("EVIL THINGS HAPPENING IN HERE MUHAHHAHAHHA")
\ No newline at end of file
diff --git a/src/api/hook.py b/src/api/hook.py
index b0ac0c4..7b7f5db 100644
--- a/src/api/hook.py
+++ b/src/api/hook.py
@@ -14,27 +14,53 @@
# along with RcGcDw. If not, see .
import logging
+import functools
+from src.configloader import settings
+from typing import Optional, Callable
import src.api.hooks
logger = logging.getLogger("src.api.hook")
-def pre_hook(func):
+
+def pre_hook(_func: Optional[Callable] = None, priority: int = 100, condition: Optional[Callable] = None, register: Optional[Callable] = None):
"""
Decorator to register a pre hook and return a function
:return: func
"""
- logger.info("{hook_name} has been registered as a pre hook.".format(hook_name=func.__name__))
- src.api.hooks.pre_hooks.append(func)
- return func
+ def decorator_prehook(func):
+ if register is None or register(settings):
+ logger.info("{hook_name} has been registered as a pre hook.".format(hook_name=func.__name__))
+ src.api.hooks.pre_hooks.append((func, priority, condition))
+
+ @functools.wraps(func)
+ def wrapper_prehook(*args, **kwargs):
+ return func(*args, **kwargs)
+ return wrapper_prehook
+
+ if _func is None:
+ return decorator_prehook
+ else:
+ return decorator_prehook(_func)
-def post_hook(func):
+def post_hook(_func: Optional[Callable] = None, priority: int = 100, condition: Optional[Callable] = None, register: Optional[Callable] = None):
"""
Decorator to register a post hook and return a function
:return: func
"""
- logger.info("{hook_name} has been registered as a post hook.".format(hook_name=func.__name__))
- src.api.hooks.post_hooks.append(func)
- return func
+ def decorator_posthook(func):
+ if register is None or register(settings):
+ logger.info("{hook_name} has been registered as a post hook.".format(hook_name=func.__name__))
+ src.api.hooks.post_hooks.append((func, priority, condition))
+
+ @functools.wraps(func)
+ def wrapper_posthook(*args, **kwargs):
+ return func(*args, **kwargs)
+ return wrapper_posthook
+
+ if _func is None:
+ return decorator_posthook
+ else:
+ return decorator_posthook(_func)
diff --git a/src/api/hooks.py b/src/api/hooks.py
index b0d7674..a8b4d3e 100644
--- a/src/api/hooks.py
+++ b/src/api/hooks.py
@@ -14,10 +14,10 @@
# along with RcGcDw. If not, see .
# Made just to avoid circular imports
-from typing import Callable, List, Dict
+from typing import Callable, List, Dict, Tuple, Optional
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.api.context import Context
formatter_hooks: Dict[str, Callable[[Context, dict], DiscordMessage]] = {}
-pre_hooks: List[Callable[[Context, dict], None]] = []
-post_hooks: List[Callable[[DiscordMessage, DiscordMessageMetadata, Context, dict], None]] = []
\ No newline at end of file
+pre_hooks: List[Tuple[Callable[[Context, dict], None], int, Optional[Callable[[Context, dict], bool]]]] = []
+post_hooks: List[Tuple[Callable[[DiscordMessage, DiscordMessageMetadata, Context, dict], None], int, Optional[Callable[[DiscordMessage, DiscordMessageMetadata, Context, dict], bool]]]] = []
\ No newline at end of file
diff --git a/src/misc.py b/src/misc.py
index 46d764f..b68cf29 100644
--- a/src/misc.py
+++ b/src/misc.py
@@ -17,9 +17,11 @@
import base64
import json, logging, sys, re, platform
from html.parser import HTMLParser
+from typing import Callable, Tuple, List, Optional, Union
from urllib.parse import urlparse, urlunparse
import requests
+from api.context import Context
from src.argparser import command_args
from src.configloader import settings
import src.api.util
@@ -314,13 +316,24 @@ def prepare_paths(path: str, dry=False):
prepare_paths(settings["wiki_url"])
-def run_hooks(hooks, *arguments):
+def run_hooks(hooks: Union[List[Tuple[Callable[[Context, dict], None], int, Optional[Callable]]], List[Tuple[Callable[[DiscordMessage, DiscordMessageMetadata, Context, dict], None], int, Optional[Callable]]]], *arguments):
for hook in hooks:
+ if hook[2] is not None:
+ try:
+ if hook[2](*arguments) is False:
+ misc_logger.debug(f"Ignoring hook {hook[0].__name__} due to conditions not being met for execution")
+ continue
+ except:
+ if settings.get("error_tolerance", 1) > 0:
+ misc_logger.exception("On running a hook check function, ignoring hook")
+ else:
+ raise
try:
- hook(*arguments)
+ misc_logger.debug(f"Running {hook[0].__name__} hook")
+ hook[0](*arguments)
except:
if settings.get("error_tolerance", 1) > 0:
- misc_logger.exception("On running a pre hook, ignoring pre-hook")
+ misc_logger.exception("On running a hook, ignoring hook")
else:
raise
diff --git a/src/rcgcdw.py b/src/rcgcdw.py
index 15d860f..c9588d8 100644
--- a/src/rcgcdw.py
+++ b/src/rcgcdw.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-
+import operator
# This file is part of Recent changes Goat compatible Discord webhook (RcGcDw).
# RcGcDw is free software: you can redistribute it and/or modify
@@ -25,7 +25,8 @@ import src.configloader
from src.migrations import *
from collections import defaultdict, Counter, OrderedDict
from src.argparser import command_args
-from typing import Optional
+from typing import Optional, Callable, Tuple
+from operator import itemgetter
import src.api.client
from src.api.context import Context
from src.api.hooks import formatter_hooks, pre_hooks, post_hooks
@@ -53,6 +54,12 @@ logger = logging.getLogger("rcgcdw")
logger.debug("Current settings: {settings}".format(settings=settings))
+def sort_hooks():
+ """Sort hooks according to their priority, first are executed hooks that have higher priority then lower"""
+ pre_hooks.sort(key=operator.itemgetter(1), reverse=True)
+ post_hooks.sort(key=operator.itemgetter(1), reverse=True)
+
+
def load_extensions():
"""Loads all of the extensions, can be a local import because all we need is them to register"""
try:
@@ -309,6 +316,7 @@ def abuselog_processing(entry):
load_extensions()
+sort_hooks()
# Log in and download wiki information
wiki = Wiki(rc_processor, abuselog_processing)
client = src.api.client.Client(formatter_hooks, wiki)