Structurize the code, fixed some issues (like not recognizing edit and new events)

This commit is contained in:
Frisk 2020-11-08 22:29:15 +01:00
parent 99a1aa7827
commit 9bc01153f8
No known key found for this signature in database
GPG key ID: 213F7C15068AF8AC
14 changed files with 340 additions and 290 deletions

0
src/__init__.py Normal file
View file

0
src/discord/__init__.py Normal file
View file

84
src/discord/message.py Normal file
View file

@ -0,0 +1,84 @@
import json
import math
import random
from collections import defaultdict
from src.configloader import settings
class DiscordMessage:
"""A class defining a typical Discord JSON representation of webhook payload."""
def __init__(self, message_type: str, event_type: str, webhook_url: str, content=None):
self.webhook_object = dict(allowed_mentions={"parse": []}, avatar_url=settings["avatars"].get(message_type, ""))
self.webhook_url = webhook_url
if message_type == "embed":
self.__setup_embed()
elif message_type == "compact":
self.webhook_object["content"] = content
self.event_type = event_type
def __setitem__(self, key, value):
"""Set item is used only in embeds."""
try:
self.embed[key] = value
except NameError:
raise TypeError("Tried to assign a value when message type is plain message!")
def __getitem__(self, item):
return self.embed[item]
def __repr__(self):
"""Return the Discord webhook object ready to be sent"""
return json.dumps(self.webhook_object)
def __setup_embed(self):
self.embed = defaultdict(dict)
if "embeds" not in self.webhook_object:
self.webhook_object["embeds"] = [self.embed]
else:
self.webhook_object["embeds"].append(self.embed)
self.embed["color"] = None
def add_embed(self):
self.finish_embed()
self.__setup_embed()
def finish_embed(self):
if self.embed["color"] is None:
if settings["appearance"]["embed"].get(self.event_type, {"color": None})["color"] is None:
self.embed["color"] = random.randrange(1, 16777215)
else:
self.embed["color"] = settings["appearance"]["embed"][self.event_type]["color"]
else:
self.embed["color"] = math.floor(self.embed["color"])
def set_author(self, name, url, icon_url=""):
self.embed["author"]["name"] = name
self.embed["author"]["url"] = url
self.embed["author"]["icon_url"] = icon_url
def add_field(self, name, value, inline=False):
if "fields" not in self.embed:
self.embed["fields"] = []
self.embed["fields"].append(dict(name=name, value=value, inline=inline))
def set_avatar(self, url):
self.webhook_object["avatar_url"] = url
def set_name(self, name):
self.webhook_object["username"] = name
class DiscordMessageMetadata:
def __init__(self, method, log_id = None, page_id = None, rev_id = None, webhook_url = None, new_data = None):
self.method = method
self.page_id = page_id
self.log_id = log_id
self.rev_id = rev_id
self.webhook_url = webhook_url
self.new_data = new_data
def dump_ids(self):
return self.page_id, self.rev_id, self.log_id

156
src/discord/queue.py Normal file
View file

@ -0,0 +1,156 @@
import re
import sys
import time
import logging
from typing import Optional
import requests
from src.configloader import settings
from src.discord.message import DiscordMessage, DiscordMessageMetadata
AUTO_SUPPRESSION_ENABLED = settings.get("auto_suppression", {"enabled": True}).get("enabled")
if AUTO_SUPPRESSION_ENABLED:
from src.fileio.database import add_entry as add_message_redaction_entry
rate_limit = 0
logger = logging.getLogger("rcgcdw.discord.queue")
class MessageQueue:
"""Message queue class for undelivered messages"""
def __init__(self):
self._queue = []
def __repr__(self):
return self._queue
def __len__(self):
return len(self._queue)
def __iter__(self):
return iter(self._queue)
def clear(self):
self._queue.clear()
def add_message(self, message):
self._queue.append(message)
def cut_messages(self, item_num):
self._queue = self._queue[item_num:]
def resend_msgs(self):
if self._queue:
logger.info(
"{} messages waiting to be delivered to Discord due to Discord throwing errors/no connection to Discord servers.".format(
len(self._queue)))
for num, item in enumerate(self._queue):
logger.debug(
"Trying to send a message to Discord from the queue with id of {} and content {}".format(str(num),
str(item)))
if send_to_discord_webhook(item[0], metadata=item[1]) < 2:
logger.debug("Sending message succeeded")
else:
logger.debug("Sending message failed")
break
else:
self.clear()
logger.debug("Queue emptied, all messages delivered")
self.cut_messages(num)
logger.debug(self._queue)
messagequeue = MessageQueue()
def handle_discord_http(code, formatted_embed, result):
if 300 > code > 199: # message went through
return 0
elif code == 400: # HTTP BAD REQUEST result.status_code, data, result, header
logger.error(
"Following message has been rejected by Discord, please submit a bug on our bugtracker adding it:")
logger.error(formatted_embed)
logger.error(result.text)
return 1
elif code == 401 or code == 404: # HTTP UNAUTHORIZED AND NOT FOUND
if result.request.method == "POST": # Ignore not found for DELETE and PATCH requests since the message could already be removed by admin
logger.error("Webhook URL is invalid or no longer in use, please replace it with proper one.")
sys.exit(1)
else:
return 0
elif code == 429:
logger.error("We are sending too many requests to the Discord, slowing down...")
return 2
elif 499 < code < 600:
logger.error(
"Discord have trouble processing the event, and because the HTTP code returned is {} it means we blame them.".format(
code))
return 3
def update_ratelimit(request):
"""Updates rate limit time"""
global rate_limit
rate_limit = 0 if int(request.headers.get('x-ratelimit-remaining', "-1")) > 0 else int(request.headers.get(
'x-ratelimit-reset-after', 0))
rate_limit += settings.get("discord_message_cooldown", 0)
def send_to_discord_webhook(data: Optional[DiscordMessage], metadata: DiscordMessageMetadata):
global rate_limit
header = settings["header"]
header['Content-Type'] = 'application/json'
standard_args = dict(headers=header)
if metadata.method == "POST":
req = requests.Request("POST", data.webhook_url+"?wait=" + "true" if AUTO_SUPPRESSION_ENABLED else "false", data=repr(data), **standard_args)
elif metadata.method == "DELETE":
req = requests.Request("DELETE", metadata.webhook_url, **standard_args)
elif metadata.method == "PATCH":
req = requests.Request("PATCH", metadata.webhook_url, data=metadata.new_data, **standard_args)
try:
time.sleep(rate_limit)
rate_limit = 0
req = req.prepare()
result = requests.Session().send(req, timeout=10)
update_ratelimit(result)
if AUTO_SUPPRESSION_ENABLED and metadata.method == "POST":
# TODO Prepare request with all of safety checks
try:
add_message_redaction_entry(*metadata.dump_ids(), result.json())
except ValueError:
logger.error("Couldn't get json of result of sending Discord message.")
except requests.exceptions.Timeout:
logger.warning("Timeouted while sending data to the webhook.")
return 3
except requests.exceptions.ConnectionError:
logger.warning("Connection error while sending the data to a webhook")
return 3
else:
return handle_discord_http(result.status_code, data, result)
def send_to_discord(data: Optional[DiscordMessage], meta: DiscordMessageMetadata):
if data is not None:
for regex in settings["disallow_regexes"]:
if data.webhook_object.get("content", None):
if re.search(re.compile(regex), data.webhook_object["content"]):
logger.info("Message {} has been rejected due to matching filter ({}).".format(data.webhook_object["content"], regex))
return # discard the message without anything
else:
for to_check in [data.webhook_object.get("description", ""), data.webhook_object.get("title", ""), *[x["value"] for x in data["fields"]], data.webhook_object.get("author", {"name": ""}).get("name", "")]:
if re.search(re.compile(regex), to_check):
logger.info("Message \"{}\" has been rejected due to matching filter ({}).".format(
to_check, regex))
return # discard the message without anything
if messagequeue:
messagequeue.add_message((data, meta))
else:
code = send_to_discord_webhook(data, metadata=meta)
if code == 3:
messagequeue.add_message((data, meta))
elif code == 2:
time.sleep(5.0)
messagequeue.add_message((data, meta))
elif code < 2:
pass

27
src/discord/redaction.py Normal file
View file

@ -0,0 +1,27 @@
import logging
from src.configloader import settings
from src.discord.message import DiscordMessageMetadata
from src.discord.queue import send_to_discord
from src.fileio.database import db_cursor, db_connection
logger = logging.getLogger("rcgcdw.discord.redaction")
def delete_messages(pageid: int):
"""Delete messages that match that pageid"""
logger.debug(type(pageid))
to_delete = db_cursor.execute("SELECT msg_id FROM event WHERE pageid = ?", (pageid,))
msg_to_remove = []
logger.debug("Deleting messages for pageid: {}".format(pageid))
for message in to_delete:
webhook_url = "{main_webhook}/messages/{message_id}".format(main_webhook=settings["webhookURL"], message_id=message[0])
msg_to_remove.append(message[0])
logger.debug("Removing following message: {}".format(message[0]))
send_to_discord(None, DiscordMessageMetadata("DELETE", webhook_url=webhook_url))
db_cursor.execute("DELETE FROM messages WHERE message_id = ?", (message[0],))
db_connection.commit()
def redact_messages(rev_ids: list, to_censor: dict):
raise NotImplemented

View file

@ -4,7 +4,9 @@ import gettext
from urllib.parse import quote_plus
from src.configloader import settings
from src.misc import link_formatter, create_article_path, DiscordMessage, send_to_discord, escape_formatting, DiscordMessageMetadata
from src.misc import link_formatter, create_article_path, escape_formatting
from src.discord.queue import send_to_discord
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.i18n import discussion_formatters
_ = discussion_formatters.gettext

View file

@ -22,7 +22,8 @@ from typing import Dict, Any
from src.configloader import settings
from src.discussion_formatters import embed_formatter, compact_formatter
from src.misc import datafile, messagequeue, prepare_paths
from src.misc import datafile, prepare_paths
from src.discord.queue import messagequeue
from src.session import session
from src.exceptions import ArticleCommentError

0
src/fileio/__init__.py Normal file
View file

51
src/fileio/database.py Normal file
View file

@ -0,0 +1,51 @@
import sqlite3
import logging
from src.configloader import settings
logger = logging.getLogger("rcgcdw.fileio.database")
def create_schema():
logger.info("Creating database schema...")
db_cursor.executescript(
"""BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "messages" (
"message_id" TEXT,
"content" TEXT
);
CREATE TABLE IF NOT EXISTS "event" (
"pageid" INTEGER,
"revid" INTEGER,
"logid" INTEGER,
"msg_id" TEXT NOT NULL,
FOREIGN KEY("msg_id") REFERENCES "messages"("message_id") ON DELETE CASCADE
);
COMMIT;""")
logger.info("Database schema has been recreated.")
def create_connection() -> (sqlite3.Connection, sqlite3.Cursor):
_db_connection = sqlite3.connect(settings['auto_suppression'].get("db_location", ':memory:'))
_db_connection.row_factory = sqlite3.Row
_db_cursor = _db_connection.cursor()
logger.debug("Database connection created")
return _db_connection, _db_cursor
def check_tables():
"""Check if tables exist, if not, create schema"""
rep = db_cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='messages';")
if not rep.fetchone():
logger.debug("No schema detected, creating schema!")
create_schema()
def add_entry(pageid: int, revid: int, logid: int, message):
"""Add an edit or log entry to the DB"""
db_cursor.execute("INSERT INTO messages (message_id, content) VALUES (?, ?)", (message.get("id"), str(message)))
db_cursor.execute("INSERT INTO event (pageid, revid, logid, msg_id) VALUES (?, ?, ?, ?)", (pageid, revid, logid, message.get("id")))
logger.debug("Adding an entry to the database (pageid: {}, revid: {}, logid: {}, message: {})".format(pageid, revid, logid, message))
db_connection.commit()
db_connection, db_cursor = create_connection()
check_tables()

View file

@ -1,65 +1,3 @@
from src.configloader import settings
from src.misc import send_to_discord, DiscordMessageMetadata
import logging
logger = logging.getLogger("rcgcdw.message_redaction")
import sqlite3
def create_schema():
logger.info("Creating database schema...")
db_cursor.executescript(
"""BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "messages" (
"message_id" TEXT,
"content" TEXT
);
CREATE TABLE IF NOT EXISTS "event" (
"pageid" INTEGER,
"revid" INTEGER,
"logid" INTEGER,
"msg_id" TEXT NOT NULL,
FOREIGN KEY("msg_id") REFERENCES "messages"("message_id") ON DELETE CASCADE
);
COMMIT;""")
logger.info("Database schema has been recreated.")
def create_connection() -> (sqlite3.Connection, sqlite3.Cursor):
_db_connection = sqlite3.connect(settings['auto_suppression'].get("db_location", ':memory:'))
_db_connection.row_factory = sqlite3.Row
_db_cursor = db_connection.cursor()
logger.debug("Database connection created")
return _db_connection, _db_cursor
def check_tables():
rep = db_cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='messages';")
if not rep.fetchone():
logger.debug("No schema detected, creating schema!")
create_schema()
def add_entry(pageid: int, revid: int, logid: int, message):
db_cursor.execute("INSERT INTO messages (message_id, content) VALUES (?, ?)", (message.get("message_id"), message))
db_cursor.execute("INSERT INTO event (pageid, revid, logid, msg_id) VALUES (?, ?, ?, ?)", (pageid, revid, logid, message.get("message_id")))
logger.debug("Adding an entry to the database (pageid: {}, revid: {}, logid: {}, message: {})".format(pageid, revid, logid, message))
def delete_messages(pageid: int):
to_delete = db_cursor.execute("SELECT msg_id FROM event WHERE pageid = ?", (pageid))
msg_to_remove = []
logger.debug("Deleting messages for pageid: {}".format(pageid))
for message in to_delete:
webhook_url = "{main_webhook}/messages/{message_id}".format(main_webhook=settings["webhookURL"], message_id=message[0])
msg_to_remove.append(message[0])
logger.debug("Removing following message: {}".format(message))
send_to_discord(None, DiscordMessageMetadata("DELETE", webhook_url=webhook_url))
db_cursor.executemany("DELETE FROM messages WHERE message_id = ?", msg_to_remove)
def redact_messages(rev_ids: list, to_censor: dict):
raise NotImplemented
db_connection, db_cursor = create_connection()
check_tables()

View file

@ -16,18 +16,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import json, logging, sys, re, time, random, math
import json, logging, sys, re
from html.parser import HTMLParser
from urllib.parse import urlparse, urlunparse, quote
import requests
from collections import defaultdict
from src.configloader import settings
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.discord.queue import messagequeue, send_to_discord
from src.i18n import misc
from typing import Optional
AUTO_SUPPRESSION_ENABLED = settings.get("auto_suppression", {"enabled": True}).get("enabled")
if AUTO_SUPPRESSION_ENABLED:
from src.message_redaction import add_entry as add_message_redaction_entry
pass
_ = misc.gettext
@ -43,7 +43,6 @@ WIKI_API_PATH: str = ""
WIKI_ARTICLE_PATH: str = ""
WIKI_SCRIPT_PATH: str = ""
WIKI_JUST_DOMAIN: str = ""
rate_limit = 0
profile_fields = {"profile-location": _("Location"), "profile-aboutme": _("About me"), "profile-link-google": _("Google link"), "profile-link-facebook":_("Facebook link"), "profile-link-twitter": _("Twitter link"), "profile-link-reddit": _("Reddit link"), "profile-link-twitch": _("Twitch link"), "profile-link-psn": _("PSN link"), "profile-link-vk": _("VK link"), "profile-link-xbl": _("XBL link"), "profile-link-steam": _("Steam link"), "profile-link-discord": _("Discord handle"), "profile-link-battlenet": _("Battle.net handle")}
@ -95,52 +94,6 @@ class DataFile:
return self.data[item]
class MessageQueue:
"""Message queue class for undelivered messages"""
def __init__(self):
self._queue = []
def __repr__(self):
return self._queue
def __len__(self):
return len(self._queue)
def __iter__(self):
return iter(self._queue)
def clear(self):
self._queue.clear()
def add_message(self, message):
self._queue.append(message)
def cut_messages(self, item_num):
self._queue = self._queue[item_num:]
def resend_msgs(self):
if self._queue:
misc_logger.info(
"{} messages waiting to be delivered to Discord due to Discord throwing errors/no connection to Discord servers.".format(
len(self._queue)))
for num, item in enumerate(self._queue):
misc_logger.debug(
"Trying to send a message to Discord from the queue with id of {} and content {}".format(str(num),
str(item)))
if send_to_discord_webhook(item[0], metadata=item[1]) < 2:
misc_logger.debug("Sending message succeeded")
else:
misc_logger.debug("Sending message failed")
break
else:
self.clear()
misc_logger.debug("Queue emptied, all messages delivered")
self.cut_messages(num)
misc_logger.debug(self._queue)
messagequeue = MessageQueue()
datafile = DataFile()
@ -245,28 +198,6 @@ def safe_read(request, *keys):
return request
def handle_discord_http(code, formatted_embed, result):
if 300 > code > 199: # message went through
return 0
elif code == 400: # HTTP BAD REQUEST result.status_code, data, result, header
misc_logger.error(
"Following message has been rejected by Discord, please submit a bug on our bugtracker adding it:")
misc_logger.error(formatted_embed)
misc_logger.error(result.text)
return 1
elif code == 401 or code == 404: # HTTP UNAUTHORIZED AND NOT FOUND
misc_logger.error("Webhook URL is invalid or no longer in use, please replace it with proper one.")
sys.exit(1)
elif code == 429:
misc_logger.error("We are sending too many requests to the Discord, slowing down...")
return 2
elif 499 < code < 600:
misc_logger.error(
"Discord have trouble processing the event, and because the HTTP code returned is {} it means we blame them.".format(
code))
return 3
def add_to_dict(dictionary, key):
if key in dictionary:
dictionary[key] += 1
@ -333,80 +264,6 @@ def send_simple(msgtype, message, name, avatar):
send_to_discord(discord_msg, meta=DiscordMessageMetadata("POST"))
def update_ratelimit(request):
"""Updates rate limit time"""
global rate_limit
rate_limit = 0 if int(request.headers.get('x-ratelimit-remaining', "-1")) > 0 else int(request.headers.get(
'x-ratelimit-reset-after', 0))
rate_limit += settings.get("discord_message_cooldown", 0)
class DiscordMessage:
"""A class defining a typical Discord JSON representation of webhook payload."""
def __init__(self, message_type: str, event_type: str, webhook_url: str, content=None):
self.webhook_object = dict(allowed_mentions={"parse": []}, avatar_url=settings["avatars"].get(message_type, ""))
self.webhook_url = webhook_url
if message_type == "embed":
self.__setup_embed()
elif message_type == "compact":
self.webhook_object["content"] = content
self.event_type = event_type
def __setitem__(self, key, value):
"""Set item is used only in embeds."""
try:
self.embed[key] = value
except NameError:
raise TypeError("Tried to assign a value when message type is plain message!")
def __getitem__(self, item):
return self.embed[item]
def __repr__(self):
"""Return the Discord webhook object ready to be sent"""
return json.dumps(self.webhook_object)
def __setup_embed(self):
self.embed = defaultdict(dict)
if "embeds" not in self.webhook_object:
self.webhook_object["embeds"] = [self.embed]
else:
self.webhook_object["embeds"].append(self.embed)
self.embed["color"] = None
def add_embed(self):
self.finish_embed()
self.__setup_embed()
def finish_embed(self):
if self.embed["color"] is None:
if settings["appearance"]["embed"].get(self.event_type, {"color": None})["color"] is None:
self.embed["color"] = random.randrange(1, 16777215)
else:
self.embed["color"] = settings["appearance"]["embed"][self.event_type]["color"]
else:
self.embed["color"] = math.floor(self.embed["color"])
def set_author(self, name, url, icon_url=""):
self.embed["author"]["name"] = name
self.embed["author"]["url"] = url
self.embed["author"]["icon_url"] = icon_url
def add_field(self, name, value, inline=False):
if "fields" not in self.embed:
self.embed["fields"] = []
self.embed["fields"].append(dict(name=name, value=value, inline=inline))
def set_avatar(self, url):
self.webhook_object["avatar_url"] = url
def set_name(self, name):
self.webhook_object["username"] = name
def profile_field_name(name, embed):
try:
return profile_fields[name]
@ -417,77 +274,6 @@ def profile_field_name(name, embed):
return _("unknown")
class DiscordMessageMetadata:
def __init__(self, method, log_id = None, page_id = None, rev_id = None, webhook_url = None, new_data = None):
self.method = method
self.page_id = page_id
self.log_id = log_id
self.rev_id = rev_id
self.webhook_url = webhook_url
self.new_data = new_data
def dump_ids(self):
return self.page_id, self.rev_id, self.log_id
def send_to_discord_webhook(data: Optional[DiscordMessage], metadata: DiscordMessageMetadata):
global rate_limit
header = settings["header"]
header['Content-Type'] = 'application/json'
standard_args = dict(headers=header, timeout=10)
if metadata.method == "POST":
req = requests.Request("POST", data.webhook_url+"?wait=" + "true" if AUTO_SUPPRESSION_ENABLED else "false", data=repr(data), **standard_args)
elif metadata.method == "DELETE":
req = requests.Request("DELETE", metadata.webhook_url, **standard_args)
elif metadata.method == "PATCH":
req = requests.Request("PATCH", metadata.webhook_url, data=metadata.new_data, **standard_args)
try:
time.sleep(rate_limit)
rate_limit = 0
req = req.prepare()
result = req.send()
update_ratelimit(result)
if AUTO_SUPPRESSION_ENABLED:
# TODO Prepare request with all of safety checks
try:
add_message_redaction_entry(*metadata.dump_ids(), result.json())
except ValueError:
misc_logger.error("Couldn't get json of result of sending Discord message.")
except requests.exceptions.Timeout:
misc_logger.warning("Timeouted while sending data to the webhook.")
return 3
except requests.exceptions.ConnectionError:
misc_logger.warning("Connection error while sending the data to a webhook")
return 3
else:
return handle_discord_http(result.status_code, data, result)
def send_to_discord(data: Optional[DiscordMessage], meta: DiscordMessageMetadata):
if data is not None:
for regex in settings["disallow_regexes"]:
if data.webhook_object.get("content", None):
if re.search(re.compile(regex), data.webhook_object["content"]):
misc_logger.info("Message {} has been rejected due to matching filter ({}).".format(data.webhook_object["content"], regex))
return # discard the message without anything
else:
for to_check in [data.webhook_object.get("description", ""), data.webhook_object.get("title", ""), *[x["value"] for x in data["fields"]], data.webhook_object.get("author", {"name": ""}).get("name", "")]:
if re.search(re.compile(regex), to_check):
misc_logger.info("Message \"{}\" has been rejected due to matching filter ({}).".format(
to_check, regex))
return # discard the message without anything
if messagequeue:
messagequeue.add_message((data, meta))
else:
code = send_to_discord_webhook(data, metadata=meta)
if code == 3:
messagequeue.add_message((data, meta))
elif code == 2:
time.sleep(5.0)
messagequeue.add_message((data, meta))
elif code < 2:
pass
class LinkParser(HTMLParser):
new_string = ""
recent_href = ""

View file

@ -6,7 +6,8 @@ import requests
from bs4 import BeautifulSoup
from src.configloader import settings
from src.misc import WIKI_SCRIPT_PATH, WIKI_API_PATH, messagequeue, datafile, send_simple, safe_read, LinkParser
from src.misc import WIKI_SCRIPT_PATH, WIKI_API_PATH, datafile, send_simple, safe_read, LinkParser
from src.discord.queue import messagequeue
from src.exceptions import MWError
from src.session import session
from src.rc_formatters import compact_formatter, embed_formatter, compact_abuselog_formatter, embed_abuselog_formatter
@ -399,11 +400,11 @@ def essential_info(change, changed_categories):
parsed_comment = None
if "userhidden" in change:
change["user"] = _("hidden")
if change.get("ns", -1) in settings.get("ignored_namespaces", ()):
return
if change["type"] in ["edit", "new"]:
logger.debug("List of categories in essential_info: {}".format(changed_categories))
identification_string = change["type"]
if change.get("ns", -1) in settings.get("ignored_namespaces", ()):
return
elif change["type"] == "log":
identification_string = "{logtype}/{logaction}".format(logtype=change["logtype"], logaction=change["logaction"])
if identification_string not in supported_logs:

View file

@ -10,10 +10,13 @@ from urllib.parse import quote_plus, quote
from bs4 import BeautifulSoup
from src.configloader import settings
from src.misc import link_formatter, create_article_path, WIKI_SCRIPT_PATH, send_to_discord, DiscordMessage, safe_read, \
WIKI_API_PATH, ContentParser, profile_field_name, LinkParser, DiscordMessageMetadata, AUTO_SUPPRESSION_ENABLED
from src.misc import link_formatter, create_article_path, WIKI_SCRIPT_PATH, safe_read, \
WIKI_API_PATH, ContentParser, profile_field_name, LinkParser, AUTO_SUPPRESSION_ENABLED
from src.discord.queue import send_to_discord
from src.discord.message import DiscordMessage, DiscordMessageMetadata
if AUTO_SUPPRESSION_ENABLED:
from src.message_redaction import delete_messages, redact_messages
from src.discord.redaction import delete_messages, redact_messages
from src.i18n import rc_formatters
#from src.rc import recent_changes, pull_comment

View file

@ -26,8 +26,9 @@ import src.misc
from collections import defaultdict, Counter
from src.configloader import settings
from src.misc import add_to_dict, datafile, \
WIKI_API_PATH, create_article_path, send_to_discord, \
DiscordMessage, DiscordMessageMetadata
WIKI_API_PATH, create_article_path
from src.discord.queue import send_to_discord
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.rc import recent_changes
from src.exceptions import MWError
from src.i18n import rcgcdw