mirror of
https://gitlab.com/chicken-riders/RcGcDw.git
synced 2025-02-23 00:24:09 +00:00
Merge branch 'hooks-context' into 'testing'
Add `feed_type` to Context See merge request piotrex43/RcGcDw!99
This commit is contained in:
commit
b4485dce38
|
@ -112,9 +112,10 @@ Context can consist of the following fields:
|
||||||
- `client` - [Client](#Client) object
|
- `client` - [Client](#Client) object
|
||||||
- `webhook_url` - string - webhook url for given formatter
|
- `webhook_url` - string - webhook url for given formatter
|
||||||
- `message_type` - string - can be either `embed` or `compact`
|
- `message_type` - string - can be either `embed` or `compact`
|
||||||
|
- `feed_type` - string - type of the feed, can be either `recentchanges`, `abuselog` or `discussion`
|
||||||
|
- `event` - string - action called, should be the same as formatter event action
|
||||||
- `categories` - {"new": set(), "removed": set()} - each set containing strings of added or removed categories for given page
|
- `categories` - {"new": set(), "removed": set()} - each set containing strings of added or removed categories for given page
|
||||||
- `parsedcomment` - string - contains escaped and Markdown parsed summary (parsed_comment) of a log/edit action
|
- `parsedcomment` - string - contains escaped and Markdown parsed summary (parsed_comment) of a log/edit action
|
||||||
- `event` - string - action called, should be the same as formatter event action
|
|
||||||
- `comment_page` - dict - containing `fullUrl` and `article` with strings both to full article url and its name
|
- `comment_page` - dict - containing `fullUrl` and `article` with strings both to full article url and its name
|
||||||
|
|
||||||
### Util
|
### Util
|
||||||
|
@ -148,4 +149,4 @@ RcGcDw implements i18n with gettext and already exposes Translations instance wi
|
||||||
**Path**: `src.api.hook`
|
**Path**: `src.api.hook`
|
||||||
There are two decorator functions available in the module: `pre_hook` and `post_hook`. They don't take arguments and simply register the function as a hook.
|
There are two decorator functions available in the module: `pre_hook` and `post_hook`. They don't take arguments and simply register the function as a hook.
|
||||||
Pre-hook functions take the following arguments: `context` ([Context object](#Context)) and `change` (dict object with change).
|
Pre-hook functions take the following arguments: `context` ([Context object](#Context)) and `change` (dict object with change).
|
||||||
Post-hook functions take the following arguments: `message` ([Discord message object](#DiscordMessage)), `metadata` ([Discord message metadata](#DiscordMessageMetadata)), `context` ([Context object](#Context)) and `change` (dictionary of main change body)
|
Post-hook functions take the following arguments: `message` ([Discord message object](#DiscordMessage)), `metadata` ([Discord message metadata](#DiscordMessageMetadata)), `context` ([Context object](#Context)) and `change` (dictionary of main change body)
|
||||||
|
|
|
@ -331,7 +331,7 @@ def embed_discussion_article_comment(ctx: Context, post: dict):
|
||||||
embed_author_discussions(post, embed)
|
embed_author_discussions(post, embed)
|
||||||
article_paths = ctx.comment_page
|
article_paths = ctx.comment_page
|
||||||
if article_paths is None:
|
if article_paths is None:
|
||||||
article_page = {"title": _("unknown"), "fullUrl": settings["fandom_discussions"]["wiki_url"]} # No page known
|
article_paths = {"title": _("unknown"), "fullUrl": settings["fandom_discussions"]["wiki_url"]} # No page known
|
||||||
if not post["isReply"]:
|
if not post["isReply"]:
|
||||||
embed.event_type = "discussion/comment/post"
|
embed.event_type = "discussion/comment/post"
|
||||||
embed["url"] = "{url}?commentId={commentId}".format(url=article_paths["fullUrl"], commentId=post["threadId"])
|
embed["url"] = "{url}?commentId={commentId}".format(url=article_paths["fullUrl"], commentId=post["threadId"])
|
||||||
|
@ -365,4 +365,4 @@ def compact_discussion_article_comment(ctx: Context, post: dict):
|
||||||
"[{author}]({author_url}) created a [reply](<{url}?commentId={commentId}&replyId={replyId}>) to a [comment](<{url}?commentId={commentId}>) on [{article}](<{url}>)").format(
|
"[{author}]({author_url}) created a [reply](<{url}?commentId={commentId}&replyId={replyId}>) to a [comment](<{url}?commentId={commentId}>) on [{article}](<{url}>)").format(
|
||||||
author=author, author_url=author_url, url=article_paths["fullUrl"], article=article_paths["title"],
|
author=author, author_url=author_url, url=article_paths["fullUrl"], article=article_paths["title"],
|
||||||
commentId=post["threadId"], replyId=post["id"])
|
commentId=post["threadId"], replyId=post["id"])
|
||||||
return DiscordMessage("compact", event_type, ctx.webhook_url, content=message)
|
return DiscordMessage("compact", event_type, ctx.webhook_url, content=message)
|
||||||
|
|
|
@ -13,4 +13,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
|
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import extensions.hooks.example_hook
|
#import extensions.hooks.example_hook
|
||||||
|
#import extensions.hooks.usertalk
|
||||||
|
#import extensions.hooks.edit_alerts
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
|
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
from src.api.hook import post_hook
|
from src.api.hook import post_hook
|
||||||
from src.configloader import settings
|
from src.configloader import settings
|
||||||
|
|
||||||
|
@ -27,6 +28,11 @@ from src.configloader import settings
|
||||||
# },
|
# },
|
||||||
# "requirements": [
|
# "requirements": [
|
||||||
# {
|
# {
|
||||||
|
# "feed": [
|
||||||
|
# "recentchanges",
|
||||||
|
# "abuselog",
|
||||||
|
# "discussion"
|
||||||
|
# ],
|
||||||
# "action": [
|
# "action": [
|
||||||
# "edit",
|
# "edit",
|
||||||
# "delete/delete",
|
# "delete/delete",
|
||||||
|
@ -40,6 +46,14 @@ from src.configloader import settings
|
||||||
# "title": [
|
# "title": [
|
||||||
# "PAGETITLE"
|
# "PAGETITLE"
|
||||||
# ],
|
# ],
|
||||||
|
# "forum": [
|
||||||
|
# "FORUMNAME",
|
||||||
|
# null
|
||||||
|
# ],
|
||||||
|
# "is_reply": null,
|
||||||
|
# "namespace": [
|
||||||
|
# 0
|
||||||
|
# ],
|
||||||
# "tags": [
|
# "tags": [
|
||||||
# ["EDIT TAG", "AND EDIT TAG"],
|
# ["EDIT TAG", "AND EDIT TAG"],
|
||||||
# ["OR EDIT TAG"]
|
# ["OR EDIT TAG"]
|
||||||
|
@ -55,6 +69,16 @@ from src.configloader import settings
|
||||||
# ["OR CATEGORY"]
|
# ["OR CATEGORY"]
|
||||||
# ]
|
# ]
|
||||||
# }
|
# }
|
||||||
|
# ],
|
||||||
|
# "filter": [
|
||||||
|
# "Section removal",
|
||||||
|
# "1"
|
||||||
|
# ],
|
||||||
|
# "af_action": [
|
||||||
|
# "edit"
|
||||||
|
# ],
|
||||||
|
# "result": [
|
||||||
|
# "disallow"
|
||||||
# ]
|
# ]
|
||||||
# }
|
# }
|
||||||
# ]
|
# ]
|
||||||
|
@ -92,28 +116,79 @@ def edit_alerts_hook(message, metadata, context, change):
|
||||||
# For every requirement, if one of the requirements passes the alert gets executed
|
# For every requirement, if one of the requirements passes the alert gets executed
|
||||||
for requirement in alert.get("requirements", []):
|
for requirement in alert.get("requirements", []):
|
||||||
try:
|
try:
|
||||||
|
req_feed = requirement.get("feed", [])
|
||||||
|
if req_feed and context.feed_type not in req_feed:
|
||||||
|
raise RequirementNotMet
|
||||||
req_action = requirement.get("action", [])
|
req_action = requirement.get("action", [])
|
||||||
# If current action isn't in config for this requirement AND current event type is not in the requirements in settings skip this requirement
|
# If current action isn't in config for this requirement AND current event type is not in the requirements in settings skip this requirement
|
||||||
if req_action and context.event not in req_action and context.event.split('/', 1)[0] not in req_action:
|
if req_action and context.event not in req_action and context.event.split('/', 1)[0] not in req_action:
|
||||||
raise RequirementNotMet
|
raise RequirementNotMet
|
||||||
req_user = requirement.get("user", [])
|
req_user = requirement.get("user", [])
|
||||||
|
change_user = None
|
||||||
|
change_anon = False
|
||||||
|
if context.feed_type == "discussion":
|
||||||
|
if change["creatorIp"]:
|
||||||
|
change_user = change["creatorIp"][1:]
|
||||||
|
change_anon = True
|
||||||
|
elif change["createdBy"]["name"]:
|
||||||
|
change_user = change["createdBy"]["name"]
|
||||||
|
change_anon = False
|
||||||
|
else:
|
||||||
|
change_user = change["user"]
|
||||||
|
if context.feed_type == "recentchanges":
|
||||||
|
change_anon = "anon" in change
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(change_user)
|
||||||
|
except ValueError:
|
||||||
|
change_anon = False
|
||||||
|
else:
|
||||||
|
change_anon = True
|
||||||
# If current user is not in config AND checkings for anon and user fail
|
# If current user is not in config AND checkings for anon and user fail
|
||||||
if req_user and change["user"] not in req_user and ("@__anon__" if "anon" in change else "@__user__") not in req_user:
|
if req_user and change_user and change_user not in req_user and ("@__anon__" if change_anon else "@__user__") not in req_user:
|
||||||
raise RequirementNotMet
|
raise RequirementNotMet
|
||||||
req_title = requirement.get("title", [])
|
req_title = requirement.get("title", [])
|
||||||
if req_title and change["title"] not in req_title:
|
change_title = change["title"]
|
||||||
|
if context.feed_type == "discussion" and change_title is None:
|
||||||
|
change_title = change["_embedded"]["thread"][0]["title"]
|
||||||
|
if change_title is None and context.comment_page is not None:
|
||||||
|
change_title = context.comment_page["title"]
|
||||||
|
if req_title and change_title not in req_title:
|
||||||
raise RequirementNotMet
|
raise RequirementNotMet
|
||||||
check_group_requirements(change.get("tags", []), requirement.get("tags", []))
|
if context.feed_type == "discussion":
|
||||||
if requirement.get("categories", []):
|
req_forum = requirement.get("forum", [])
|
||||||
for req_cats in requirement.get("categories", []):
|
if req_forum and change["forumName"] not in req_forum:
|
||||||
try:
|
raise RequirementNotMet
|
||||||
check_group_requirements(context.categories.new, req_cats.get("added", []))
|
req_reply = requirement.get("is_reply", None)
|
||||||
check_group_requirements(context.categories.removed, req_cats.get("removed", []))
|
if req_reply is not None and change["isReply"] == req_reply:
|
||||||
except RequirementNotMet:
|
raise RequirementNotMet
|
||||||
continue
|
else:
|
||||||
|
req_namespace = requirement.get("namespace", [])
|
||||||
|
if req_namespace and change["ns"] not in req_namespace:
|
||||||
|
raise RequirementNotMet
|
||||||
|
if context.feed_type == "recentchanges":
|
||||||
|
check_group_requirements(change.get("tags", []), requirement.get("tags", []))
|
||||||
|
if requirement.get("categories", []):
|
||||||
|
for req_cats in requirement.get("categories", []):
|
||||||
|
try:
|
||||||
|
check_group_requirements(context.categories.new, req_cats.get("added", []))
|
||||||
|
check_group_requirements(context.categories.removed, req_cats.get("removed", []))
|
||||||
|
except RequirementNotMet:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
break
|
raise RequirementNotMet
|
||||||
else:
|
elif context.feed_type == "abuselog":
|
||||||
|
req_filter = requirement.get("filter", [])
|
||||||
|
# Allow both filter id and name as id might be hidden when logged out
|
||||||
|
if req_filter and change["filter"] not in req_filter and change["filter_id"] not in req_filter:
|
||||||
|
raise RequirementNotMet
|
||||||
|
af_action = requirement.get("af_action", [])
|
||||||
|
if af_action and change["action"] not in af_action:
|
||||||
|
raise RequirementNotMet
|
||||||
|
req_result = requirement.get("result", [])
|
||||||
|
if req_result and change["result"] not in req_result:
|
||||||
raise RequirementNotMet
|
raise RequirementNotMet
|
||||||
except RequirementNotMet:
|
except RequirementNotMet:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -25,14 +25,29 @@ from src.configloader import settings
|
||||||
# }
|
# }
|
||||||
discord_users = settings.get("hooks", {}).get("usertalk", {})
|
discord_users = settings.get("hooks", {}).get("usertalk", {})
|
||||||
|
|
||||||
|
def add_mention(message, userid):
|
||||||
|
"""This function adds a mention for the userid"""
|
||||||
|
message.webhook_object["content"] = (message.webhook_object.get("content", "") or "") + " <@{}>".format(userid)
|
||||||
|
if message.webhook_object["allowed_mentions"].get("users", []):
|
||||||
|
if userid not in message.webhook_object["allowed_mentions"]["users"]:
|
||||||
|
message.webhook_object["allowed_mentions"]["users"].append(userid)
|
||||||
|
else:
|
||||||
|
message.webhook_object["allowed_mentions"]["users"] = [userid]
|
||||||
|
|
||||||
@post_hook
|
@post_hook
|
||||||
def usertalk_hook(message, metadata, context, change):
|
def usertalk_hook(message, metadata, context, change):
|
||||||
if discord_users and change["ns"] in [2, 3, 202] and not "/" in change["title"]:
|
if not discord_users:
|
||||||
|
return
|
||||||
|
if context.feed_type in ["recentchanges", "abuselog"] and change["ns"] in [2, 3, 202, 1200] and "/" not in change["title"]:
|
||||||
username = change["title"].split(':', 1)[1]
|
username = change["title"].split(':', 1)[1]
|
||||||
if discord_users.get(username, "") and username != change["user"]:
|
if discord_users.get(username, "") and username != change["user"]:
|
||||||
message.webhook_object["content"] = (message.webhook_object.get("content", "") or "") + " <@{}>".format(discord_users[username])
|
add_mention(message, discord_users[username])
|
||||||
if message.webhook_object["allowed_mentions"].get("users", []):
|
elif context.feed_type == "discussion" and context.event == "discussion/wall" and change["forumName"].endswith(' Message Wall'):
|
||||||
if discord_users[username] not in message.webhook_object["allowed_mentions"]["users"]:
|
username = change["forumName"][:-13]
|
||||||
message.webhook_object["allowed_mentions"]["users"].append(discord_users[username])
|
author = None
|
||||||
else:
|
if change["creatorIp"]:
|
||||||
message.webhook_object["allowed_mentions"]["users"] = [discord_users[username]]
|
author = change["creatorIp"][1:]
|
||||||
|
elif change["createdBy"]["name"]:
|
||||||
|
author = change["createdBy"]["name"]
|
||||||
|
if discord_users.get(username, "") and username != author:
|
||||||
|
add_mention(message, discord_users[username])
|
||||||
|
|
|
@ -22,10 +22,11 @@ if TYPE_CHECKING:
|
||||||
class Context:
|
class Context:
|
||||||
"""Context object containing client and some metadata regarding specific formatter call,
|
"""Context object containing client and some metadata regarding specific formatter call,
|
||||||
they are mainly used as a bridge between part that fetches the changes and API's formatters"""
|
they are mainly used as a bridge between part that fetches the changes and API's formatters"""
|
||||||
def __init__(self, message_type: str, webhook_url: str, client: Client):
|
def __init__(self, message_type: str, feed_type: str, webhook_url: str, client: Client):
|
||||||
self.client = client
|
self.client = client
|
||||||
self.webhook_url = webhook_url
|
self.webhook_url = webhook_url
|
||||||
self.message_type = message_type
|
self.message_type = message_type
|
||||||
|
self.feed_type = feed_type
|
||||||
self.categories = None
|
self.categories = None
|
||||||
self.parsedcomment = None
|
self.parsedcomment = None
|
||||||
self.event = None
|
self.event = None
|
||||||
|
@ -41,4 +42,4 @@ class Context:
|
||||||
self.comment_page = page
|
self.comment_page = page
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"<Context message_type={self.message_type} event={self.event} webhook_url={self.webhook_url}"
|
return f"<Context message_type={self.message_type} feed_type={self.feed_type} event={self.event} webhook_url={self.webhook_url}"
|
||||||
|
|
|
@ -107,7 +107,7 @@ def parse_discussion_post(post, comment_pages):
|
||||||
"""Initial post recognition & handling"""
|
"""Initial post recognition & handling"""
|
||||||
global client
|
global client
|
||||||
post_type = post["_embedded"]["thread"][0]["containerType"]
|
post_type = post["_embedded"]["thread"][0]["containerType"]
|
||||||
context = Context(display_mode, webhook_url, client)
|
context = Context(display_mode, "discussion", webhook_url, client)
|
||||||
# Filter posts by forum
|
# Filter posts by forum
|
||||||
if post_type == "FORUM" and settings["fandom_discussions"].get("show_forums", []):
|
if post_type == "FORUM" and settings["fandom_discussions"].get("show_forums", []):
|
||||||
if not post["forumName"] in settings["fandom_discussions"]["show_forums"]:
|
if not post["forumName"] in settings["fandom_discussions"]["show_forums"]:
|
||||||
|
|
|
@ -202,10 +202,10 @@ def rc_processor(change, changed_categories):
|
||||||
metadata = DiscordMessageMetadata("POST", rev_id=change.get("revid", None), log_id=change.get("logid", None),
|
metadata = DiscordMessageMetadata("POST", rev_id=change.get("revid", None), log_id=change.get("logid", None),
|
||||||
page_id=change.get("pageid", None))
|
page_id=change.get("pageid", None))
|
||||||
logger.debug(change)
|
logger.debug(change)
|
||||||
context = Context(settings["appearance"]["mode"], settings["webhookURL"], client)
|
context = Context(settings["appearance"]["mode"], "recentchanges", settings["webhookURL"], client)
|
||||||
run_hooks(pre_hooks, context, change)
|
|
||||||
if ("actionhidden" in change or "suppressed" in change) and "suppressed" not in settings["ignored"]: # if event is hidden using suppression
|
if ("actionhidden" in change or "suppressed" in change) and "suppressed" not in settings["ignored"]: # if event is hidden using suppression
|
||||||
context.event = "suppressed"
|
context.event = "suppressed"
|
||||||
|
run_hooks(pre_hooks, context, change)
|
||||||
try:
|
try:
|
||||||
discord_message: Optional[DiscordMessage] = default_message("suppressed", formatter_hooks)(context, change)
|
discord_message: Optional[DiscordMessage] = default_message("suppressed", formatter_hooks)(context, change)
|
||||||
except NoFormatter:
|
except NoFormatter:
|
||||||
|
@ -242,6 +242,7 @@ def rc_processor(change, changed_categories):
|
||||||
if identification_string in settings["ignored"]:
|
if identification_string in settings["ignored"]:
|
||||||
return
|
return
|
||||||
context.event = identification_string
|
context.event = identification_string
|
||||||
|
run_hooks(pre_hooks, context, change)
|
||||||
try:
|
try:
|
||||||
discord_message: Optional[DiscordMessage] = default_message(identification_string, formatter_hooks)(context, change)
|
discord_message: Optional[DiscordMessage] = default_message(identification_string, formatter_hooks)(context, change)
|
||||||
except:
|
except:
|
||||||
|
@ -278,9 +279,9 @@ def abuselog_processing(entry):
|
||||||
action = "abuselog"
|
action = "abuselog"
|
||||||
if action in settings["ignored"]:
|
if action in settings["ignored"]:
|
||||||
return
|
return
|
||||||
context = Context(settings["appearance"]["mode"], settings["webhookURL"], client)
|
context = Context(settings["appearance"]["mode"], "abuselog", settings["webhookURL"], client)
|
||||||
run_hooks(pre_hooks, context, entry)
|
|
||||||
context.event = action
|
context.event = action
|
||||||
|
run_hooks(pre_hooks, context, entry)
|
||||||
try:
|
try:
|
||||||
discord_message: Optional[DiscordMessage] = default_message(action, formatter_hooks)(context, entry)
|
discord_message: Optional[DiscordMessage] = default_message(action, formatter_hooks)(context, entry)
|
||||||
except NoFormatter:
|
except NoFormatter:
|
||||||
|
|
Loading…
Reference in a new issue