# This file is part of Recent changes Goat compatible Discord webhook (RcGcDw). # # RcGcDw is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RcGcDw is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with RcGcDw. If not, see . import ipaddress import logging import math import re import time import datetime from collections import OrderedDict from src.discord.message import DiscordMessage from src.api import formatter from src.api.context import Context from src.api.util import embed_helper, sanitize_to_url, parse_mediawiki_changes, clean_link, compact_author, \ sanitize_to_markdown, compact_summary from src.exceptions import * logger = logging.getLogger("extensions.base") if 1 == 2: # additional translation strings in unreachable code # noinspection PyUnreachableCode _ = lambda a: a # noinspection PyUnreachableCode print(_("director"), _("bot"), _("editor"), _("directors"), _("sysop"), _("bureaucrat"), _("reviewer"), _("autoreview"), _("autopatrol"), _("wiki_guardian"), _.pgettext("Removal of content field in Revision Delete", "content"), _.pgettext("Removal of comment field in Revision Delete", "comment"), _.pgettext("Removal of user field in Revision Delete", "user")) # Page edit - event edit, New - page creation @formatter.embed(event="edit", mode="embed", aliases=["new"]) def embed_edit(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) action = ctx.event editsize = change["newlen"] - change["oldlen"] if editsize > 0: embed["color"] = min(65280, 35840 + (math.floor(editsize / 52)) * 256) # Choose shade of green elif editsize < 0: embed["color"] = min(16711680, 9175040 + (math.floor(abs(editsize) / 52)) * 65536) # Choose shade of red elif editsize == 0: embed["color"] = 8750469 if change["title"].startswith("MediaWiki:Tag-"): # Refresh tag list when tag display name is edited ctx.client.refresh_internal_data() # Sparse is better than dense. # Readability counts. embed["url"] = "{wiki}index.php?title={article}&curid={pageid}&diff={diff}&oldid={oldrev}".format( wiki=ctx.client.WIKI_SCRIPT_PATH, pageid=change["pageid"], diff=change["revid"], oldrev=change["old_revid"], article=sanitize_to_url(change["title"]) ) embed["title"] = "{redirect}{article} ({new}{minor}{bot}{space}{editsize})".format( redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["title"]), editsize="+" + str(editsize) if editsize > 0 else editsize, new=ctx._("(N!) ") if action == "new" else "", minor=ctx._("m") if action == "edit" and change.get("minor", False) else "", bot=ctx._('b') if change.get("bot", False) else "", space=" " if "bot" in change or (action == "edit" and change.get("minor", False)) or action == "new" else "") if ctx.settings["appearance"]["embed"]["show_edit_changes"]: try: if action == "new": changed_content = ctx.client.make_api_request( "?action=compare&format=json&formatversion=2&fromslots=main&torev={diff}&fromtext-main=&topst=1&prop=diff".format( diff=change["revid"]), "compare", "body") else: changed_content = ctx.client.make_api_request( "?action=compare&format=json&formatversion=2&fromrev={oldrev}&torev={diff}&topst=1&prop=diff".format( diff=change["revid"], oldrev=change["old_revid"]), "compare", "body") except (ServerError, MediaWikiError): changed_content = None if changed_content: ctx.changed_content = changed_content parse_mediawiki_changes(ctx, changed_content, embed) else: logger.warning("Unable to download data on the edit content!") embed_helper(ctx, embed, change) return embed @formatter.compact(event="edit", mode="compact", aliases=["new"]) def compact_edit(ctx: Context, change: dict) -> DiscordMessage: parsed_comment = compact_summary(ctx) author, author_url = compact_author(ctx, change) action = ctx.event edit_link = clean_link("{wiki}index.php?title={article}&curid={pageid}&diff={diff}&oldid={oldrev}".format( wiki=ctx.client.WIKI_SCRIPT_PATH, pageid=change["pageid"], diff=change["revid"], oldrev=change["old_revid"], article=sanitize_to_url(change["title"]))) logger.debug(edit_link) edit_size = change["newlen"] - change["oldlen"] sign = "" if edit_size > 0: sign = "+" bold = "" if abs(edit_size) > 500: bold = "**" if action == "edit": content = ctx._( "[{author}]({author_url}) edited [{article}]({edit_link}){comment} {bold}({sign}{edit_size}){bold}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), edit_link=edit_link, comment=parsed_comment, edit_size=edit_size, sign=sign, bold=bold) else: content = ctx._( "[{author}]({author_url}) created [{article}]({edit_link}){comment} {bold}({sign}{edit_size}){bold}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), edit_link=edit_link, comment=parsed_comment, edit_size=edit_size, sign=sign, bold=bold) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # Upload - upload/reupload, upload/upload @formatter.embed(event="upload/upload", mode="embed", aliases=["upload/overwrite", "upload/revert"]) def embed_upload_upload(ctx: Context, change: dict) -> DiscordMessage: license = None embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) action = ctx.event # Requesting more information on the image request_for_image_data = None try: params = OrderedDict() params["action"] = "query" params["format"] = "json" if ctx.settings["license_detection"] and action == "upload/upload": params["prop"] = "imageinfo|revisions" params["rvprop"] = "content" params["rvslots"] = "main" else: params["prop"] = "imageinfo" params["titles"] = change["title"] params["iiprop"] = "timestamp|url|archivename" params["iilimit"] = "5" request_for_image_data = ctx.client.make_api_request(params, "query", "pages") except (ServerError, MediaWikiError): logger.exception( "Couldn't retrieve more information about the image {} because of server/MediaWiki error".format( change["title"])) except (ClientError, BadRequest): raise except KeyError: logger.exception( "Couldn't retrieve more information about the image {} because of unknown error".format( change["title"])) else: if not request_for_image_data[0].get("missing", False): # Image still exists and not removed image_data = request_for_image_data[0] else: logger.warning("Request for additional image information have failed. The preview will not be shown.") request_for_image_data = None embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) image_direct_url = None # Make a request for file revisions so we can get direct URL to the image for embed if request_for_image_data is not None: try: ctx.image_data = image_data urls = image_data["imageinfo"] for num, revision in enumerate(urls): if revision["timestamp"] == change["logparams"][ "img_timestamp"]: # find the correct revision corresponding for this log entry image_direct_url = "{rev}{c}rcgcdw={cache}".format(rev=revision["url"], c="&" if "?" in revision["url"] else "?", cache=int(time.time() * 5)) # cachebusting break except KeyError: logger.exception( "Wiki did not respond with extended information about file. The preview will not be shown.") else: logger.warning("Request for additional image information have failed. The preview will not be shown.") if action in ("upload/overwrite", "upload/revert"): if image_direct_url: try: revision = image_data["imageinfo"][num + 1] except IndexError: logger.exception( "Could not analize the information about the image (does it have only one version when expected more in overwrite?) which resulted in no Options field: {}".format( image_data["imageinfo"])) else: undolink = "{wiki}index.php?title={filename}&action=revert&oldimage={archiveid}".format( wiki=ctx.client.WIKI_SCRIPT_PATH, filename=sanitize_to_url(change["title"]), archiveid=revision["archivename"]) embed.add_field(ctx._("Options"), ctx._("([preview]({link}) | [undo]({undolink}))").format( link=image_direct_url, undolink=undolink)) if ctx.settings["appearance"]["embed"]["embed_images"]: embed["image"]["url"] = image_direct_url if action == "upload/overwrite": embed["title"] = ctx._("Uploaded a new version of {name}").format(name=sanitize_to_markdown(change["title"])) elif action == "upload/revert": embed["title"] = ctx._("Reverted a version of {name}").format(name=sanitize_to_markdown(change["title"])) else: embed["title"] = ctx._("Uploaded {name}").format(name=sanitize_to_markdown(change["title"])) if ctx.settings["license_detection"] and image_direct_url: try: content = image_data['revisions'][0]["slots"]["main"]['content'] matches = re.search(re.compile(ctx.settings["license_regex"], re.IGNORECASE), content) if matches is not None: license = matches.group("license") else: if re.search(re.compile(ctx.settings["license_regex_detect"], re.IGNORECASE), content) is None: license = ctx._("**No license!**") else: license = "?" except IndexError: logger.error( "Given regex for the license detection is incorrect. It does not have a capturing group called \"license\" specified. Please fix license_regex value in the config!") license = "?" except re.error: logger.error( "Given regex for the license detection is incorrect. Please fix license_regex or license_regex_detect values in the config!") license = "?" except KeyError: logger.exception( "Unknown error when retriefing the image data for a license, full content: {}".format(image_data)) if image_direct_url: embed.add_field(ctx._("Options"), ctx._("([preview]({link}))").format(link=image_direct_url)) if ctx.settings["appearance"]["embed"]["embed_images"]: embed["image"]["url"] = image_direct_url embed_helper(ctx, embed, change) if license is not None: embed["description"] += ctx._("\nLicense: {}").format(license) return embed @formatter.compact(event="upload/revert", mode="compact") def compact_upload_revert(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) file_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) reverted a version of [{file}]({file_link}){comment}").format( author=author, author_url=author_url, file=sanitize_to_markdown(change["title"]), file_link=file_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) @formatter.compact(event="upload/overwrite", mode="compact") def compact_upload_overwrite(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) file_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) uploaded a new version of [{file}]({file_link}){comment}").format( author=author, author_url=author_url, file=sanitize_to_markdown(change["title"]), file_link=file_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) @formatter.compact(event="upload/upload", mode="compact") def compact_upload_upload(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) file_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) uploaded [{file}]({file_link}){comment}").format(author=author, author_url=author_url, file=sanitize_to_markdown( change["title"]), file_link=file_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # delete/delete - Page deletion @formatter.embed(event="delete/delete", mode="embed") def embed_delete_delete(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed['url'] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Deleted page {article}").format(article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="delete/delete", mode="compact") def compact_delete_delete(ctx: Context, change: dict) -> DiscordMessage: parsed_comment = compact_summary(ctx) author, author_url = compact_author(ctx, change) page_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) content = ctx._("[{author}]({author_url}) deleted [{page}]({page_link}){comment}").format(author=author, author_url=author_url, page=sanitize_to_markdown( change["title"]), page_link=page_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # delete/delete_redir - Redirect deletion @formatter.embed(event="delete/delete_redir", mode="embed") def embed_delete_delete_redir(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed['url'] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Deleted redirect {article} by overwriting").format( article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="delete/delete_redir", mode="compact") def compact_delete_delete_redir(ctx: Context, change: dict) -> DiscordMessage: page_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) deleted redirect by overwriting [{page}]({page_link}){comment}").format( author=author, author_url=author_url, page=sanitize_to_markdown(change["title"]), page_link=page_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # delete/restore - Restoring a page @formatter.embed(event="delete/restore", mode="embed") def embed_delete_restore(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed['url'] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Restored {article}").format(article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="delete/restore", mode="compact") def compact_delete_restore(ctx: Context, change: dict) -> DiscordMessage: page_link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) restored [{article}]({article_url}){comment}").format(author=author, author_url=author_url, article=sanitize_to_markdown( change["title"]), article_url=page_link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # delete/event - Deleting an event with revdelete feature @formatter.embed(event="delete/event", mode="embed") def embed_delete_event(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed['url'] = ctx.client.create_article_path("Special:RecentChanges") embed["title"] = ctx._("Changed visibility of log events") return embed @formatter.compact(event="delete/event", mode="compact") def compact_delete_event(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) changed visibility of log events{comment}").format(author=author, author_url=author_url, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # delete/revision - Deleting revision information @formatter.embed(event="delete/revision", mode="embed") def embed_delete_revision(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) amount = len(change["logparams"]["ids"]) embed['url'] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx.ngettext("Changed visibility of revision on page {article} ", "Changed visibility of {amount} revisions on page {article} ", amount).format( article=sanitize_to_markdown(change["title"]), amount=amount) embed["description"] += ctx.parsedcomment + ctx._("\nContents have been suppressed") old_hidden_fields = {ctx._(key) for key, value in change["logparams"]["old"].items() if (key in ("content", "comment", "user") and value is True)} new_hidden_fields = {ctx._(key) for key, value in change["logparams"]["new"].items() if (key in ("content", "comment", "user") and value is True)} if new_hidden_fields - old_hidden_fields: embed.add_field(ctx._("Hidden fields"), ctx._(", ").join(new_hidden_fields - old_hidden_fields)) if old_hidden_fields - new_hidden_fields: embed.add_field(ctx._("Revealed fields"), ctx._(", ").join(old_hidden_fields - new_hidden_fields)) return embed @formatter.compact(event="delete/revision", mode="compact") def compact_delete_revision(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) amount = len(change["logparams"]["ids"]) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx.ngettext( "[{author}]({author_url}) changed visibility of revision on page [{article}]({article_url}){comment}", "[{author}]({author_url}) changed visibility of {amount} revisions on page [{article}]({article_url}){comment}", amount).format(author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, amount=amount, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # move/move - Moving pages @formatter.embed(event="move/move", mode="embed") def embed_move_move(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["logparams"]['target_title'])) embed["description"] = "{supress}. {desc}".format(desc=ctx.parsedcomment, supress=ctx._("No redirect has been made") if change["logparams"].get("suppressredirect", False) else ctx._("A redirect has been made")) embed["title"] = ctx._("Moved {redirect}{article} to {target}").format(redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["title"]), target=sanitize_to_markdown( change["logparams"]['target_title'])) return embed @formatter.compact(event="move/move", mode="compact") def compact_move_move(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["logparams"]['target_title']))) redirect_status = ctx._("without making a redirect") if change["logparams"].get("suppressredirect", False) else ctx._( "with a redirect") parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) moved {redirect}*{article}* to [{target}]({target_url}) {made_a_redirect}{comment}").format( author=author, author_url=author_url, redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["title"]), target=sanitize_to_markdown(change["logparams"]['target_title']), target_url=link, comment=parsed_comment, made_a_redirect=redirect_status) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # move/move_redir - Move over redirect @formatter.embed(event="move/move_redir", mode="embed") def embed_move_move_redir(ctx: Context, change: dict) -> DiscordMessage: embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["logparams"]['target_title'])) embed["description"] = "{supress}. {desc}".format(desc=ctx.parsedcomment, supress=ctx._("No redirect has been made") if change["logparams"].get("suppressredirect", False) else ctx._("A redirect has been made")) embed["title"] = ctx._("Moved {redirect}{article} to {title} over redirect").format( redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["title"]), title=sanitize_to_markdown(change["logparams"]["target_title"])) return embed @formatter.compact(event="move/move_redir", mode="compact") def compact_move_move_redir(ctx: Context, change: dict) -> DiscordMessage: author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["logparams"]['target_title']))) redirect_status = ctx._("without making a redirect") if change["logparams"].get("suppressredirect", False) else ctx._( "with a redirect") parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) moved {redirect}*{article}* over redirect to [{target}]({target_url}) {made_a_redirect}{comment}").format( author=author, author_url=author_url, redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["title"]), target=sanitize_to_markdown(change["logparams"]['target_title']), target_url=link, comment=parsed_comment, made_a_redirect=redirect_status) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # protect/move_prot - Moving protection @formatter.embed(event="protect/move_prot", mode="embed") def embed_protect_move_prot(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["logparams"]["oldtitle_title"])) embed["title"] = ctx._("Moved protection settings from {redirect}{article} to {title}").format( redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["logparams"]["oldtitle_title"]), title=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="protect/move_prot", mode="compact") def compact_protect_move_prot(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["logparams"]["oldtitle_title"]))) parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) moved protection settings from {redirect}*{article}* to [{target}]({target_url}){comment}").format( author=author, author_url=author_url, redirect="⤷ " if change.get("redirect", False) else "", article=sanitize_to_markdown(change["logparams"]["oldtitle_title"]), target=sanitize_to_markdown(change["title"]), target_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # protect/protect - Creating protection @formatter.embed(event="protect/protect", mode="embed") def embed_protect_protect(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Protected {target}").format(target=sanitize_to_markdown(change["title"])) embed["description"] = "{settings}{cascade} | {reason}".format( settings=sanitize_to_markdown(change["logparams"].get("description", "")), cascade=ctx._(" [cascading]") if change["logparams"].get("cascade", False) else "", reason=ctx.parsedcomment) return embed @formatter.compact(event="protect/protect", mode="compact") def compact_protect_protect(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) protected [{article}]({article_url}) with the following settings: {settings}{comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, settings=change["logparams"].get("description", "") + ( ctx._(" [cascading]") if change["logparams"].get("cascade", False) else ""), comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # protect/modify - Changing protection settings @formatter.embed(event="protect/modify", mode="embed") def embed_protect_modify(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Changed protection level for {article}").format(article=sanitize_to_markdown(change["title"])) embed["description"] = "{settings}{cascade} | {reason}".format( settings=sanitize_to_markdown(change["logparams"].get("description", "")), cascade=ctx._(" [cascading]") if change["logparams"].get("cascade", False) else "", reason=ctx.parsedcomment) return embed @formatter.compact(event="protect/modify", mode="compact") def compact_protect_modify(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) modified protection settings of [{article}]({article_url}) to: {settings}{comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, settings=sanitize_to_markdown(change["logparams"].get("description", "")) + ( ctx._(" [cascading]") if change["logparams"].get("cascade", False) else ""), comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # protect/unprotect - Unprotecting a page @formatter.embed(event="protect/unprotect", mode="embed") def embed_protect_unprotect(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Removed protection from {article}").format(article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="protect/unprotect", mode="compact") def compact_protect_unprotect(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) removed protection from [{article}]({article_url}){comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # block/block - Blocking an user def block_expiry(change: dict, ctx: Context) -> str: if change["logparams"]["duration"] in ["infinite", "indefinite", "infinity", "never"]: return ctx._("for infinity and beyond") else: if "expiry" in change["logparams"]: expiry_date_time_obj = datetime.datetime.strptime(change["logparams"]["expiry"], '%Y-%m-%dT%H:%M:%SZ') timestamp_date_time_obj = datetime.datetime.strptime(change["timestamp"], '%Y-%m-%dT%H:%M:%SZ') timedelta_for_expiry = (expiry_date_time_obj - timestamp_date_time_obj).total_seconds() elif isinstance(change["logparams"]["duration"], int): timedelta_for_expiry = change["logparams"]["duration"] else: return change["logparams"]["duration"] # Temporary? Should be rare? We will see in testing years, days, hours, minutes = timedelta_for_expiry // 31557600, timedelta_for_expiry % 31557600 // 86400, \ timedelta_for_expiry % 86400 // 3600, timedelta_for_expiry % 3600 // 60 if not any([years, days, hours, minutes]): return ctx._("for less than a minute") time_names = ( ctx.ngettext("year", "years", years), ctx.ngettext("day", "days", days), ctx.ngettext("hour", "hours", hours), ctx.ngettext("minute", "minutes", minutes)) final_time = [] for num, timev in enumerate([years, days, hours, minutes]): if timev: final_time.append( ctx._("for {time_number} {time_unit}").format(time_unit=time_names[num], time_number=int(timev))) return ctx._(", ").join(final_time) @formatter.embed(event="block/block", mode="embed") def embed_block_block(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) user = change["title"].split(':', 1)[1] try: if "/" not in user: ipaddress.ip_address(user) embed["url"] = ctx.client.create_article_path("Special:Contributions/{user}".format(user=user)) except ValueError: embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) if change["logparams"].get("sitewide", False) is False: restriction_description = "" if "restrictions" in change["logparams"]: if "pages" in change["logparams"]["restrictions"] and change["logparams"]["restrictions"]["pages"]: restriction_description = ctx._("Blocked from editing the following pages: ") restricted_pages = ["*" + i["page_title"] + "*" for i in change["logparams"]["restrictions"]["pages"]] restriction_description = restriction_description + ", ".join(restricted_pages) if "namespaces" in change["logparams"]["restrictions"] and change["logparams"]["restrictions"][ "namespaces"]: namespaces = [] if restriction_description: restriction_description = restriction_description + ctx._(" and namespaces: ") else: restriction_description = ctx._("Blocked from editing pages on following namespaces: ") for namespace in change["logparams"]["restrictions"]["namespaces"]: if str(namespace) == "0": namespaces.append("*{ns}*".format(ns=ctx._("(Main)"))) elif str(namespace) in ctx.client.namespaces: # if we have cached namespace name for given namespace number, add its name to the list namespaces.append("*{ns}*".format(ns=ctx.client.namespaces[str(namespace)]["name"])) else: namespaces.append("*{ns}*".format(ns=namespace)) restriction_description = restriction_description + ", ".join(namespaces) restriction_description = restriction_description + "." if len(restriction_description) > 1020: logger.debug(restriction_description) restriction_description = restriction_description[:1020] + "…" embed.add_field(ctx._("Partial block details"), restriction_description, inline=True) block_flags = change["logparams"].get("flags") if block_flags: embed.add_field(ctx._("Block flags"), ", ".join( block_flags)) # TODO Translate flags into MW messages, this requires making additional request in init_request since we want to get all messages with prefix (amprefix) block-log-flags- and that parameter is exclusive with ammessages embed["title"] = ctx._("Blocked {blocked_user} {time}").format(blocked_user=user, time=block_expiry(change, ctx)) embed_helper(ctx, embed, change) return embed @formatter.compact(event="block/block", mode="compact") def compact_block_block(ctx: Context, change: dict): user = change["title"].split(':', 1)[1] restriction_description = "" author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) try: if "/" not in user: ipaddress.ip_address(user) link = clean_link(ctx.client.create_article_path("Special:Contributions/{user}".format(user=user))) except ValueError: link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) if "sitewide" not in change["logparams"]: if "restrictions" in change["logparams"]: if "pages" in change["logparams"]["restrictions"] and change["logparams"]["restrictions"]["pages"]: restriction_description = ctx._(" on pages: ") restricted_pages = ["*{page}*".format(page=i["page_title"]) for i in change["logparams"]["restrictions"]["pages"]] restriction_description = restriction_description + ", ".join(restricted_pages) if "namespaces" in change["logparams"]["restrictions"] and change["logparams"]["restrictions"][ "namespaces"]: namespaces = [] if restriction_description: restriction_description = restriction_description + ctx._(" and namespaces: ") else: restriction_description = ctx._(" on namespaces: ") for namespace in change["logparams"]["restrictions"]["namespaces"]: if str(namespace) == "0": namespaces.append("*{ns}*".format(ns=ctx._("(Main)"))) elif str(namespace) in ctx.client.namespaces: # if we have cached namespace name for given namespace number, add its name to the list namespaces.append("*{ns}*".format(ns=ctx.client.namespaces[str(namespace)]["name"])) else: namespaces.append("*{ns}*".format(ns=namespace)) restriction_description = restriction_description + ", ".join(namespaces) restriction_description = restriction_description + "." if len(restriction_description) > 1020: logger.debug(restriction_description) restriction_description = restriction_description[:1020] + "…" content = ctx._( "[{author}]({author_url}) blocked [{user}]({user_url}) {time}{restriction_desc}{comment}").format(author=author, author_url=author_url, user=sanitize_to_markdown(user), time=block_expiry( change, ctx), user_url=link, restriction_desc=restriction_description, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # block/reblock - Changing settings of a block @formatter.embed(event="block/reblock", mode="embed") def embed_block_reblock(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) user = change["title"].split(':', 1)[1] embed["title"] = ctx._("Changed block settings for {blocked_user}").format(blocked_user=sanitize_to_markdown(user)) return embed @formatter.compact(event="block/reblock") def compact_block_reblock(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) user = change["title"].split(':', 1)[1] parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) changed block settings for [{blocked_user}]({user_url}){comment}").format( author=author, author_url=author_url, blocked_user=sanitize_to_markdown(user), user_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # block/unblock - Unblocking an user @formatter.embed(event="block/unblock", mode="embed") def embed_block_unblock(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) user = change["title"].split(':', 1)[1] embed["title"] = ctx._("Unblocked {blocked_user}").format(blocked_user=sanitize_to_markdown(user)) return embed @formatter.compact(event="block/unblock") def compact_block_unblock(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) user = change["title"].split(':', 1)[1] parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) unblocked [{blocked_user}]({user_url}){comment}").format(author=author, author_url=author_url, blocked_user=sanitize_to_markdown(user), user_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # suppressed - Custom event for whenever there is limited information available about the event due to revdel @formatter.embed(event="suppressed", mode="embed") def embed_suppressed(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed["url"] = ctx.client.create_article_path("") embed["title"] = ctx._("Action has been hidden by administration") embed["author"]["name"] = ctx._("Unknown") return embed @formatter.compact(event="suppressed", mode="compact") def compact_suppressed(ctx: Context, change: dict): content = ctx._("An action has been hidden by administration.") return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # import/upload - Importing pages by uploading exported XML files @formatter.embed(event="import/upload", mode="embed") def embed_import_upload(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx.ngettext("Imported {article} with {count} revision", "Imported {article} with {count} revisions", change["logparams"]["count"]).format( article=sanitize_to_markdown(change["title"]), count=change["logparams"]["count"]) return embed @formatter.compact(event="import/upload", mode="compact") def compact_import_upload(ctx: Context, change: dict): link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) content = ctx.ngettext("[{author}]({author_url}) imported [{article}]({article_url}) with {count} revision{comment}", "[{author}]({author_url}) imported [{article}]({article_url}) with {count} revisions{comment}", change["logparams"]["count"]).format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, count=change["logparams"]["count"], comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # import/interwiki - Importing interwiki entries @formatter.embed(event="import/interwiki", mode="embed") def embed_import_interwiki(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) if "count" in change["logparams"] and "interwiki_title" in change["logparams"]: embed["title"] = ctx.ngettext("Imported {article} with {count} revision from \"{source}\"", "Imported {article} with {count} revisions from \"{source}\"", change["logparams"]["count"]).format( article=sanitize_to_markdown(change["title"]), count=change["logparams"]["count"], source=sanitize_to_markdown(change["logparams"]["interwiki_title"])) else: embed["title"] = ctx._("Imported {article}").format(article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="import/interwiki", mode="compact") def compact_import_interwiki(ctx: Context, change: dict): link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) if "count" in change["logparams"] and "interwiki_title" in change["logparams"]: source_link = clean_link(ctx.client.create_article_path(change["logparams"]["interwiki_title"])) content = ctx.ngettext( "[{author}]({author_url}) imported [{article}]({article_url}) with {count} revision from [{source}]({source_url}){comment}", "[{author}]({author_url}) imported [{article}]({article_url}) with {count} revisions from [{source}]({source_url}){comment}", change["logparams"]["count"]).format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, count=change["logparams"]["count"], source=sanitize_to_markdown(change["logparams"]["interwiki_title"]), source_url=source_link, comment=parsed_comment) else: content = ctx._("[{author}]({author_url}) imported [{article}]({article_url}){comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # rights/rights - Assigning rights groups def get_changed_groups(change: dict, ctx: Context) -> [[str], [str]]: """Creates strings comparing the changes between the user groups for the user""" def expiry_parse_time(passed_time): try: return ctx._(" (until {date_and_time})").format(date_and_time=datetime.datetime.strptime(passed_time, "%Y-%m-%dT%H:%M:%SZ").strftime("%Y-%m-%d %H:%M:%S UTC")) except ValueError: return "" new_group_meta = {ctx._(t["group"]): expiry_parse_time(t.get("expiry", "infinity")) for t in change["logparams"].get("newmetadata", [])} # translate all groups and pull them into a set old_groups = {ctx._(x) for x in change["logparams"]["oldgroups"]} new_groups = {ctx._(x) for x in change["logparams"]["newgroups"]} added = [x + new_group_meta.get(x, "") for x in new_groups - old_groups] removed = [x for x in old_groups - new_groups] return added, removed @formatter.embed(event="rights/rights", aliases=["rights/autopromote"]) def embed_rights_rights(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed["url"] = ctx.client.create_article_path(sanitize_to_url("User:{}".format(change["title"].split(":")[1]))) if ctx.event == "rights/rights": embed["title"] = ctx._("Changed group membership for {target}").format(target=sanitize_to_markdown(change["title"].split(":")[1])) else: embed.set_author(ctx._("System"), "") embed["title"] = ctx._("{target} got autopromoted to a new usergroup").format( target=sanitize_to_markdown(change["title"].split(":")[1])) # if len(change["logparams"]["oldgroups"]) < len(change["logparams"]["newgroups"]): # embed["thumbnail"]["url"] = "https://i.imgur.com/WnGhF5g.gif" added, removed = get_changed_groups(change, ctx) if added: embed.add_field(ctx.ngettext("Added group", "Added groups", len(added)), "\n".join(added), inline=True) if removed: embed.add_field(ctx.ngettext("Removed group", "Removed groups", len(removed)), "\n".join(removed), inline=True) embed_helper(ctx, embed, change) return embed @formatter.compact(event="rights/rights", aliases=["rights/autopromote"]) def compact_rights_rights(ctx: Context, change: dict): link = clean_link(ctx.client.create_article_path(sanitize_to_url("User:{user}".format(user=change["title"].split(":")[1])))) added, removed = get_changed_groups(change, ctx) author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) if ctx.event == "rights/rights": group_changes = "Unknown group changes." # Because I don't know if it can handle time extensions correctly if added and removed: group_changes = ctx._("Added to {added} and removed from {removed}.").format( added=ctx._(", ").join(added), removed=ctx._(", ").join(removed)) elif added: group_changes = ctx._("Added to {added}.").format(added=ctx._(", ").join(added)) elif removed: group_changes = ctx._("Removed from {removed}.").format(removed=ctx._(", ").join(removed)) content = ctx._("[{author}]({author_url}) changed group membership for [{target}]({target_url}): {group_changes}{comment}").format( author=author, author_url=author_url, target=sanitize_to_markdown(change["title"].split(":")[1]), target_url=link, group_changes=group_changes, comment=parsed_comment) else: content = ctx._("The system autopromoted [{target}]({target_url}) to {added}.{comment}").format( target=sanitize_to_markdown(change["title"].split(":")[1]), target_url=link, added=ctx._(", ").join(added), comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # merge/merge - Merging histories of two pages @formatter.embed(event="merge/merge", mode="embed") def embed_merge_merge(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Merged revision histories of {article} into {dest}").format( article=sanitize_to_markdown(change["title"]), dest=sanitize_to_markdown(change["logparams"][ "dest_title"])) return embed @formatter.compact(event="merge/merge") def compact_merge_merge(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) link_dest = clean_link(ctx.client.create_article_path(sanitize_to_url(change["logparams"]["dest_title"]))) content = ctx._( "[{author}]({author_url}) merged revision histories of [{article}]({article_url}) into [{dest}]({dest_url}){comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, dest_url=link_dest, dest=sanitize_to_markdown(change["logparams"]["dest_title"]), comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # newusers/autocreate - Auto creation of user account @formatter.embed(event="newusers/autocreate", mode="embed") def embed_newusers_autocreate(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created account automatically") return embed @formatter.compact(event="newusers/autocreate") def compact_newusers_autocreate(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) content = ctx._("Account [{author}]({author_url}) was created automatically").format(author=author, author_url=author_url) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # newusers/create - Auto creation of user account @formatter.embed(event="newusers/create", mode="embed") def embed_newusers_create(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created account") return embed @formatter.compact(event="newusers/create") def compact_newusers_create(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) content = ctx._("Account [{author}]({author_url}) was created").format(author=author, author_url=author_url) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # newusers/autocreate - Auto creation of user account @formatter.embed(event="newusers/create2", mode="embed") def embed_newusers_create2(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created account {article}").format(article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="newusers/create2") def compact_newusers_create2(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) content = ctx._("Account [{article}]({article_url}) was created by [{author}]({author_url}){comment}").format( article=sanitize_to_markdown(change["title"]), article_url=link, author=author, author_url=author_url, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # newusers/byemail - Creation of account by email @formatter.embed(event="newusers/byemail", mode="embed") def embed_newusers_byemail(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created account {article} and password was sent by email").format( article=sanitize_to_markdown(change["title"])) return embed @formatter.compact(event="newusers/byemail") def compact_newusers_byemail(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) parsed_comment = compact_summary(ctx) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) content = ctx._( "Account [{article}]({article_url}) was created by [{author}]({author_url}) and password was sent by email{comment}").format( article=sanitize_to_markdown(change["title"]), article_url=link, author=author, author_url=author_url, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # newusers/newusers - New users @formatter.embed(event="newusers/newusers", mode="embed") def embed_newusers_newusers(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url("User:{}".format(change["user"]))) embed["title"] = ctx._("Created account") return embed @formatter.compact(event="newusers/newusers") def compact_newusers_newusers(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) content = ctx._("Account [{author}]({author_url}) was created").format(author=author, author_url=author_url) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # contentmodel/change - Changing the content model of a page @formatter.embed(event="contentmodel/change", mode="embed") def embed_contentmodel_change(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Changed the content model of the page {article}").format( article=sanitize_to_markdown(change["title"])) embed["description"] = ctx._("Model changed from {old} to {new}: {reason}").format(old=change["logparams"]["oldmodel"], new=change["logparams"]["newmodel"], reason=ctx.parsedcomment) return embed @formatter.compact(event="contentmodel/change") def compact_contentmodel_change(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) changed the content model of the page [{article}]({article_url}) from {old} to {new}{comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, old=change["logparams"]["oldmodel"], new=change["logparams"]["newmodel"], comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # contentmodel/new - Creating a page with non-default content model @formatter.embed(event="contentmodel/new", mode="embed") def embed_contentmodel_new(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change, set_desc=False) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created the page {article} using a non-default content model").format( article=sanitize_to_markdown(change["title"])) embed["description"] = ctx._("Created with model {new}: {reason}").format(new=change["logparams"]["newmodel"], reason=ctx.parsedcomment) return embed @formatter.compact(event="contentmodel/new") def compact_contentmodel_new(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._( "[{author}]({author_url}) created the page [{article}]({article_url}) using a non-default content model {new}{comment}").format( author=author, author_url=author_url, article=sanitize_to_markdown(change["title"]), article_url=link, new=change["logparams"]["newmodel"], comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # managetags/create - Creating log tags @formatter.embed(event="managetags/create", mode="embed") def embed_managetags_create(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) ctx.client.refresh_internal_data() embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Created the tag \"{tag}\"").format(tag=sanitize_to_markdown(change["logparams"]["tag"])) return embed @formatter.compact(event="managetags/create") def compact_managetags_create(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) ctx.client.refresh_internal_data() parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) created the [tag]({tag_url}) \"{tag}\"{comment}").format(author=author, author_url=author_url, tag= sanitize_to_markdown( change[ "logparams"][ "tag"]), tag_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # managetags/delete - Deleting a tag @formatter.embed(event="managetags/delete", mode="embed") def embed_managetags_delete(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) ctx.client.refresh_internal_data() embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Deleted the tag \"{tag}\"").format(tag=sanitize_to_markdown(change["logparams"]["tag"])) if change["logparams"]["count"] > 0: embed.add_field(ctx._('Removed from'), ctx.ngettext("{} revision or log entry", "{} revisions and/or log entries", change["logparams"]["count"]).format(change["logparams"]["count"])) embed_helper(ctx, embed, change) return embed @formatter.compact(event="managetags/delete") def compact_managetags_delete(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) ctx.client.refresh_internal_data() parsed_comment = compact_summary(ctx) if change["logparams"]["count"] == 0: content = ctx._("[{author}]({author_url}) deleted the [tag]({tag_url}) \"{tag}\"{comment}").format(author=author, author_url=author_url, tag=sanitize_to_markdown( change[ "logparams"][ "tag"]), tag_url=link, comment=parsed_comment) else: content = ctx.ngettext( "[{author}]({author_url}) deleted the [tag]({tag_url}) \"{tag}\" and removed it from {count} revision or log entry{comment}", "[{author}]({author_url}) deleted the [tag]({tag_url}) \"{tag}\" and removed it from {count} revisions and/or log entries{comment}", change["logparams"]["count"]).format(author=author, author_url=author_url, tag=sanitize_to_markdown(change["logparams"]["tag"]), tag_url=link, count=change["logparams"]["count"], comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # managetags/activate - Activating a tag @formatter.embed(event="managetags/activate", mode="embed") def embed_managetags_activate(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Activated the tag \"{tag}\"").format(tag=sanitize_to_markdown(change["logparams"]["tag"])) return embed @formatter.compact(event="managetags/activate") def compact_managetags_activate(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) activated the [tag]({tag_url}) \"{tag}\"{comment}").format(author=author, author_url=author_url, tag=sanitize_to_markdown( change[ "logparams"][ "tag"]), tag_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content) # managetags/deactivate - Deactivating a tag @formatter.embed(event="managetags/deactivate", mode="embed") def embed_managetags_deactivate(ctx: Context, change: dict): embed = DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url) embed_helper(ctx, embed, change) embed["url"] = ctx.client.create_article_path(sanitize_to_url(change["title"])) embed["title"] = ctx._("Deactivated the tag \"{tag}\"").format(tag=sanitize_to_markdown(change["logparams"]["tag"])) return embed @formatter.compact(event="managetags/deactivate") def compact_managetags_deactivate(ctx: Context, change: dict): author, author_url = compact_author(ctx, change) link = clean_link(ctx.client.create_article_path(sanitize_to_url(change["title"]))) parsed_comment = compact_summary(ctx) content = ctx._("[{author}]({author_url}) deactivated the [tag]({tag_url}) \"{tag}\"{comment}").format(author=author, author_url=author_url, tag=sanitize_to_markdown( change[ "logparams"][ "tag"]), tag_url=link, comment=parsed_comment) return DiscordMessage(ctx.message_type, ctx.event, ctx.webhook_url, content=content)