Commiting broken code

This commit is contained in:
Frisk 2020-07-21 14:15:40 +02:00
parent 217c30d096
commit 76ef334843
No known key found for this signature in database
GPG key ID: 213F7C15068AF8AC
9 changed files with 158 additions and 82 deletions

View file

@ -7,7 +7,7 @@ from src.misc import get_paths
from src.exceptions import * from src.exceptions import *
from src.database import db_cursor from src.database import db_cursor
from collections import defaultdict from collections import defaultdict
from queue_handler import DBHandler from src.queue_handler import DBHandler
logging.config.dictConfig(settings["logging"]) logging.config.dictConfig(settings["logging"])
logger = logging.getLogger("rcgcdb.bot") logger = logging.getLogger("rcgcdb.bot")
@ -35,7 +35,7 @@ def calculate_delay() -> float:
return min_delay return min_delay
def generate_targets(wiki_url: str) -> defaultdict[list]: def generate_targets(wiki_url: str) -> defaultdict:
combinations = defaultdict(list) combinations = defaultdict(list)
for webhook in db_cursor.execute('SELECT ROWID, * FROM rcgcdw WHERE wiki = ?', wiki_url): for webhook in db_cursor.execute('SELECT ROWID, * FROM rcgcdw WHERE wiki = ?', wiki_url):
# rowid, guild, configid, webhook, wiki, lang, display, rcid, wikiid, postid # rowid, guild, configid, webhook, wiki, lang, display, rcid, wikiid, postid
@ -50,13 +50,14 @@ async def wiki_scanner():
for db_wiki in db_cursor.execute('SELECT * FROM rcgcdw GROUP BY wiki'): for db_wiki in db_cursor.execute('SELECT * FROM rcgcdw GROUP BY wiki'):
extended = False extended = False
if db_wiki[3] not in all_wikis: if db_wiki[3] not in all_wikis:
logger.debug("New wiki: {}".format(wiki[1])) logger.debug("New wiki: {}".format(db_wiki[3]))
all_wikis[db_wiki[3]] = Wiki() all_wikis[db_wiki[3]] = Wiki()
local_wiki = all_wikis[db_wiki[3]] # set a reference to a wiki object from memory local_wiki = all_wikis[db_wiki[3]] # set a reference to a wiki object from memory
if local_wiki.mw_messages is None: if local_wiki.mw_messages is None:
extended = True extended = True
logger.debug("test")
try: try:
wiki_response = await local_wiki.fetch_wiki(extended, db_wiki[0]) wiki_response = await local_wiki.fetch_wiki(extended, db_wiki[3])
await local_wiki.check_status(wiki[3], wiki_response.status) await local_wiki.check_status(wiki[3], wiki_response.status)
except (WikiServerError, WikiError): except (WikiServerError, WikiError):
continue # ignore this wiki if it throws errors continue # ignore this wiki if it throws errors
@ -83,9 +84,13 @@ async def wiki_scanner():
for change in recent_changes: # Yeah, second loop since the categories require to be all loaded up for change in recent_changes: # Yeah, second loop since the categories require to be all loaded up
if change["rcid"] < db_wiki[6]: if change["rcid"] < db_wiki[6]:
for target in targets.items(): for target in targets.items():
await essential_info(change, categorize_events, local_wiki, db_wiki, target, paths) await essential_info(change, categorize_events, local_wiki, db_wiki, target, paths, recent_changes_resp)
if recent_changes:
DBHandler.add(db_wiki[3], change["rcid"])
await asyncio.sleep(delay=calc_delay) await asyncio.sleep(delay=calc_delay)
DBHandler.update_db()
async def message_sender(): async def message_sender():
pass pass
@ -95,4 +100,5 @@ async def main_loop():
task1 = asyncio.create_task(wiki_scanner()) task1 = asyncio.create_task(wiki_scanner())
task2 = asyncio.create_task(message_sender()) task2 = asyncio.create_task(message_sender())
asyncio.run(main_loop()) asyncio.run(main_loop())

View file

@ -1,7 +1,7 @@
import json, sys, logging import json, sys, logging
try: # load settings try: # load settings
with open("../settings.json") as sfile: with open("settings.json") as sfile:
settings = json.load(sfile) settings = json.load(sfile)
if "user-agent" in settings["header"]: if "user-agent" in settings["header"]:
settings["header"]["user-agent"] = settings["header"]["user-agent"].format(version="1.0") # set the version in the useragent settings["header"]["user-agent"] = settings["header"]["user-agent"].format(version="1.0") # set the version in the useragent

View file

@ -5,8 +5,6 @@ from src.database import db_cursor
logger = logging.getLogger("rcgcdb.discord") logger = logging.getLogger("rcgcdb.discord")
reasons = {410: _("wiki deletion"), 404: _("wiki deletion"), 401: _("wiki becoming inaccessible"), 402: _("wiki becoming inaccessible"), 403: _("wiki becoming inaccessible")}
# General functions # General functions
class DiscordMessage(): class DiscordMessage():
"""A class defining a typical Discord JSON representation of webhook payload.""" """A class defining a typical Discord JSON representation of webhook payload."""
@ -71,7 +69,9 @@ class DiscordMessage():
# User facing webhook functions # User facing webhook functions
def wiki_removal(wiki_id, status): def wiki_removal(wiki_id, status): # TODO Add lang selector
reasons = {410: _("wiki deletion"), 404: _("wiki deletion"), 401: _("wiki becoming inaccessible"),
402: _("wiki becoming inaccessible"), 403: _("wiki becoming inaccessible")}
reason = reasons.get(status, _("unknown error")) reason = reasons.get(status, _("unknown error"))
for observer in db_cursor.execute('SELECT * FROM observers WHERE wiki_id = ?', wiki_id): for observer in db_cursor.execute('SELECT * FROM observers WHERE wiki_id = ?', wiki_id):
DiscordMessage("compact", "webhook/remove", webhook_url=observer[4], content=_("The webhook for {} has been removed due to {}.".format(reason))) # TODO DiscordMessage("compact", "webhook/remove", webhook_url=observer[4], content=_("The webhook for {} has been removed due to {}.".format(reason))) # TODO

View file

@ -4,9 +4,10 @@ import re
import time import time
import logging import logging
import base64 import base64
from config import settings from src.config import settings
from src.misc import link_formatter, create_article_path, LinkParser, profile_field_name, ContentParser, DiscordMessage, safe_read from src.misc import link_formatter, create_article_path, LinkParser, profile_field_name, ContentParser, DiscordMessage, safe_read
from urllib.parse import quote_plus from urllib.parse import quote_plus
from src.msgqueue import send_to_discord
# from html.parser import HTMLParser # from html.parser import HTMLParser
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@ -20,7 +21,10 @@ from src.i18n import langs
logger = logging.getLogger("rcgcdw.rc_formatters") logger = logging.getLogger("rcgcdw.rc_formatters")
#from src.rcgcdw import recent_changes, ngettext, logger, profile_field_name, LinkParser, pull_comment #from src.rcgcdw import recent_changes, ngettext, logger, profile_field_name, LinkParser, pull_comment
async def compact_formatter(action, change, parsed_comment, categories, recent_changes, target, _, ngettext, paths): async def compact_formatter(action, change, parsed_comment, categories, recent_changes, target, _, ngettext, paths,
additional_data=None):
if additional_data is None:
additional_data = {"namespaces": {}, "tags": {}}
WIKI_API_PATH = paths[0] WIKI_API_PATH = paths[0]
WIKI_SCRIPT_PATH = paths[1] WIKI_SCRIPT_PATH = paths[1]
WIKI_ARTICLE_PATH = paths[2] WIKI_ARTICLE_PATH = paths[2]
@ -121,8 +125,8 @@ async def compact_formatter(action, change, parsed_comment, categories, recent_c
else: else:
restriction_description = _(" on namespaces: ") restriction_description = _(" on namespaces: ")
for namespace in change["logparams"]["restrictions"]["namespaces"]: for namespace in change["logparams"]["restrictions"]["namespaces"]:
if str(namespace) in recent_changes.namespaces: # if we have cached namespace name for given namespace number, add its name to the list if str(namespace) in additional_data.namespaces: # if we have cached namespace name for given namespace number, add its name to the list
namespaces.append("*{ns}*".format(ns=recent_changes.namespaces[str(namespace)]["*"])) namespaces.append("*{ns}*".format(ns=additional_data.namespaces[str(namespace)]["*"]))
else: else:
namespaces.append("*{ns}*".format(ns=namespace)) namespaces.append("*{ns}*".format(ns=namespace))
restriction_description = restriction_description + ", ".join(namespaces) restriction_description = restriction_description + ", ".join(namespaces)
@ -177,7 +181,7 @@ async def compact_formatter(action, change, parsed_comment, categories, recent_c
content = _("[{author}]({author_url}) edited the {field} on {target} profile. *({desc})*").format(author=author, content = _("[{author}]({author_url}) edited the {field} on {target} profile. *({desc})*").format(author=author,
author_url=author_url, author_url=author_url,
target=target, target=target,
field=profile_field_name(change["logparams"]['4:section'], False), field=profile_field_name(change["logparams"]['4:section'], False, _),
desc=BeautifulSoup(change["parsedcomment"], "lxml").get_text()) desc=BeautifulSoup(change["parsedcomment"], "lxml").get_text())
elif action in ("rights/rights", "rights/autopromote"): elif action in ("rights/rights", "rights/autopromote"):
link = link_formatter(create_article_path("User:{user}".format(user=change["title"].split(":")[1]), WIKI_ARTICLE_PATH)) link = link_formatter(create_article_path("User:{user}".format(user=change["title"].split(":")[1]), WIKI_ARTICLE_PATH))
@ -310,16 +314,18 @@ async def compact_formatter(action, change, parsed_comment, categories, recent_c
else: else:
logger.warning("No entry for {event} with params: {params}".format(event=action, params=change)) logger.warning("No entry for {event} with params: {params}".format(event=action, params=change))
return return
send_to_discord(DiscordMessage("compact", action, target[1], content=content)) await send_to_discord(DiscordMessage("compact", action, target[1], content=content, wiki=WIKI_SCRIPT_PATH))
async def embed_formatter(action, change, parsed_comment, categories, recent_changes, target, _, ngettext, paths): async def embed_formatter(action, change, parsed_comment, categories, recent_changes, target, _, ngettext, paths, additional_data=None):
if additional_data is None:
additional_data = {"namespaces": {}, "tags": {}}
WIKI_API_PATH = paths[0] WIKI_API_PATH = paths[0]
WIKI_SCRIPT_PATH = paths[1] WIKI_SCRIPT_PATH = paths[1]
WIKI_ARTICLE_PATH = paths[2] WIKI_ARTICLE_PATH = paths[2]
WIKI_JUST_DOMAIN = paths[3] WIKI_JUST_DOMAIN = paths[3]
LinkParser = LinkParser() LinkParser = LinkParser()
embed = DiscordMessage("embed", action, target[1]) embed = DiscordMessage("embed", action, target[1], wiki=WIKI_SCRIPT_PATH)
if parsed_comment is None: if parsed_comment is None:
parsed_comment = _("No description provided") parsed_comment = _("No description provided")
if action != "suppressed": if action != "suppressed":
@ -350,7 +356,7 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
embed["title"] = "{redirect}{article} ({new}{minor}{bot}{space}{editsize})".format(redirect="" if "redirect" in change else "", article=change["title"], editsize="+" + str( embed["title"] = "{redirect}{article} ({new}{minor}{bot}{space}{editsize})".format(redirect="" if "redirect" in change else "", article=change["title"], editsize="+" + str(
editsize) if editsize > 0 else editsize, new=_("(N!) ") if action == "new" else "", editsize) if editsize > 0 else editsize, new=_("(N!) ") if action == "new" else "",
minor=_("m") if action == "edit" and "minor" in change else "", bot=_('b') if "bot" in change else "", space=" " if "bot" in change or (action == "edit" and "minor" in change) or action == "new" else "") minor=_("m") if action == "edit" and "minor" in change else "", bot=_('b') if "bot" in change else "", space=" " if "bot" in change or (action == "edit" and "minor" in change) or action == "new" else "")
if settings["appearance"]["embed"]["show_edit_changes"]: if target[1] == 3:
if action == "new": if action == "new":
changed_content = await safe_read(await recent_changes.safe_request( changed_content = await safe_read(await recent_changes.safe_request(
"{wiki}?action=compare&format=json&fromtext=&torev={diff}&topst=1&prop=diff".format( "{wiki}?action=compare&format=json&fromtext=&torev={diff}&topst=1&prop=diff".format(
@ -362,7 +368,7 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
wiki=WIKI_API_PATH, diff=change["revid"],oldrev=change["old_revid"] wiki=WIKI_API_PATH, diff=change["revid"],oldrev=change["old_revid"]
)), "compare", "*") )), "compare", "*")
if changed_content: if changed_content:
EditDiff = ContentParser() EditDiff = ContentParser(_)
EditDiff.feed(changed_content) EditDiff.feed(changed_content)
if EditDiff.small_prev_del: if EditDiff.small_prev_del:
if EditDiff.small_prev_del.replace("~~", "").isspace(): if EditDiff.small_prev_del.replace("~~", "").isspace():
@ -422,37 +428,9 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
embed["title"] = _("Reverted a version of {name}").format(name=change["title"]) embed["title"] = _("Reverted a version of {name}").format(name=change["title"])
else: else:
embed["title"] = _("Uploaded {name}").format(name=change["title"]) embed["title"] = _("Uploaded {name}").format(name=change["title"])
if settings["license_detection"]:
article_content = await safe_read(await recent_changes.safe_request(
"{wiki}?action=query&format=json&prop=revisions&titles={article}&rvprop=content".format(
wiki=WIKI_API_PATH, article=quote_plus(change["title"], safe=''))), "query", "pages")
if article_content is None:
logger.warning("Something went wrong when getting license for the image")
return 0
if "-1" not in article_content:
content = list(article_content.values())[0]['revisions'][0]['*']
try:
matches = re.search(re.compile(settings["license_regex"], re.IGNORECASE), content)
if matches is not None:
license = matches.group("license")
else:
if re.search(re.compile(settings["license_regex_detect"], re.IGNORECASE), content) is None:
license = _("**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 = "?"
if license is not None:
parsed_comment += _("\nLicense: {}").format(license)
if additional_info_retrieved: if additional_info_retrieved:
embed.add_field(_("Options"), _("([preview]({link}))").format(link=image_direct_url)) embed.add_field(_("Options"), _("([preview]({link}))").format(link=image_direct_url))
if settings["appearance"]["embed"]["embed_images"]: if target[1] > 1:
embed["image"]["url"] = image_direct_url embed["image"]["url"] = image_direct_url
elif action == "delete/delete": elif action == "delete/delete":
link = create_article_path(change["title"].replace(" ", "_"), WIKI_ARTICLE_PATH) link = create_article_path(change["title"].replace(" ", "_"), WIKI_ARTICLE_PATH)
@ -506,8 +484,8 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
else: else:
restriction_description = _("Blocked from editing pages on following namespaces: ") restriction_description = _("Blocked from editing pages on following namespaces: ")
for namespace in change["logparams"]["restrictions"]["namespaces"]: for namespace in change["logparams"]["restrictions"]["namespaces"]:
if str(namespace) in recent_changes.namespaces: # if we have cached namespace name for given namespace number, add its name to the list if str(namespace) in additional_data.namespaces: # if we have cached namespace name for given namespace number, add its name to the list
namespaces.append("*{ns}*".format(ns=recent_changes.namespaces[str(namespace)]["*"])) namespaces.append("*{ns}*".format(ns=additional_data.namespaces[str(namespace)]["*"]))
else: else:
namespaces.append("*{ns}*".format(ns=namespace)) namespaces.append("*{ns}*".format(ns=namespace))
restriction_description = restriction_description + ", ".join(namespaces) restriction_description = restriction_description + ", ".join(namespaces)
@ -527,22 +505,22 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
embed["title"] = _("Unblocked {blocked_user}").format(blocked_user=user) embed["title"] = _("Unblocked {blocked_user}").format(blocked_user=user)
elif action == "curseprofile/comment-created": elif action == "curseprofile/comment-created":
if settings["appearance"]["embed"]["show_edit_changes"]: if settings["appearance"]["embed"]["show_edit_changes"]:
parsed_comment = recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_ARTICLE_PATH) parsed_comment = await recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_API_PATH)
link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"])) link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"]), WIKI_ARTICLE_PATH)
embed["title"] = _("Left a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \ embed["title"] = _("Left a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \
change["user"] else _( change["user"] else _(
"Left a comment on their own profile") "Left a comment on their own profile")
elif action == "curseprofile/comment-replied": elif action == "curseprofile/comment-replied":
if settings["appearance"]["embed"]["show_edit_changes"]: if settings["appearance"]["embed"]["show_edit_changes"]:
parsed_comment = recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_ARTICLE_PATH) parsed_comment = await recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_API_PATH)
link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"])) link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"]), WIKI_ARTICLE_PATH)
embed["title"] = _("Replied to a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \ embed["title"] = _("Replied to a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \
change["user"] else _( change["user"] else _(
"Replied to a comment on their own profile") "Replied to a comment on their own profile")
elif action == "curseprofile/comment-edited": elif action == "curseprofile/comment-edited":
if settings["appearance"]["embed"]["show_edit_changes"]: if settings["appearance"]["embed"]["show_edit_changes"]:
parsed_comment = recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_ARTICLE_PATH) parsed_comment = await recent_changes.pull_comment(change["logparams"]["4:comment_id"], WIKI_API_PATH)
link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"])) link = create_article_path("Special:CommentPermalink/{commentid}".format(commentid=change["logparams"]["4:comment_id"]), WIKI_ARTICLE_PATH)
embed["title"] = _("Edited a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \ embed["title"] = _("Edited a comment on {target}'s profile").format(target=change["title"].split(':')[1]) if change["title"].split(':')[1] != \
change["user"] else _( change["user"] else _(
"Edited a comment on their own profile") "Edited a comment on their own profile")
@ -716,11 +694,11 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
if "tags" in change and change["tags"]: if "tags" in change and change["tags"]:
tag_displayname = [] tag_displayname = []
for tag in change["tags"]: for tag in change["tags"]:
if tag in recent_changes.tags: if tag in additional_data.tags:
if recent_changes.tags[tag] is None: if additional_data.tags[tag] is None:
continue # Ignore hidden tags continue # Ignore hidden tags
else: else:
tag_displayname.append(recent_changes.tags[tag]) tag_displayname.append(additional_data.tags[tag])
else: else:
tag_displayname.append(tag) tag_displayname.append(tag)
embed.add_field(_("Tags"), ", ".join(tag_displayname)) embed.add_field(_("Tags"), ", ".join(tag_displayname))
@ -730,4 +708,4 @@ async def embed_formatter(action, change, parsed_comment, categories, recent_cha
del_cat = (_("**Removed**: ") + ", ".join(list(categories["removed"])[0:16]) + ("" if len(categories["removed"])<=15 else _(" and {} more").format(len(categories["removed"])-15))) if categories["removed"] else "" del_cat = (_("**Removed**: ") + ", ".join(list(categories["removed"])[0:16]) + ("" if len(categories["removed"])<=15 else _(" and {} more").format(len(categories["removed"])-15))) if categories["removed"] else ""
embed.add_field(_("Changed categories"), new_cat + del_cat) embed.add_field(_("Changed categories"), new_cat + del_cat)
embed.finish_embed() embed.finish_embed()
send_to_discord(embed) await send_to_discord(embed)

View file

@ -9,14 +9,14 @@ import random
from urllib.parse import urlparse, urlunparse from urllib.parse import urlparse, urlunparse
import math import math
import aiohttp import aiohttp
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")}
logger = logging.getLogger("rcgcdw.misc") logger = logging.getLogger("rcgcdw.misc")
class DiscordMessage(): class DiscordMessage():
"""A class defining a typical Discord JSON representation of webhook payload.""" """A class defining a typical Discord JSON representation of webhook payload."""
def __init__(self, message_type: str, event_type: str, webhook_url: list, content=None): def __init__(self, message_type: str, event_type: str, webhook_url: list, wiki, content=None):
self.webhook_object = dict(allowed_mentions={"parse": []}, avatar_url=settings["avatars"].get(message_type, "")) self.webhook_object = dict(allowed_mentions={"parse": []}, avatar_url=settings["avatars"].get(message_type, ""))
self.webhook_url = webhook_url self.webhook_url = webhook_url
self.wiki = wiki
if message_type == "embed": if message_type == "embed":
self.__setup_embed() self.__setup_embed()
@ -76,9 +76,46 @@ class DiscordMessage():
def set_name(self, name): def set_name(self, name):
self.webhook_object["username"] = name self.webhook_object["username"] = name
async def send_to_discord_webhook(data: DiscordMessage, session: aiohttp.ClientSession):
header = settings["header"]
header['Content-Type'] = 'application/json'
for webhook in data.webhook_url:
try:
result = await session.post("https://discord.com/api/webhooks/"+webhook, data=repr(data),
headers=header)
except (aiohttp.ClientConnectionError, aiohttp.ServerConnectionError):
logger.exception("Could not send the message to Discord")
return 3
return await handle_discord_http(result.status, repr(data), await result.text())
async 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
logger.error("Webhook URL is invalid or no longer in use, please replace it with proper one.")
return 1
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 get_paths(wiki: str, request) -> tuple: def get_paths(wiki: str, request) -> tuple:
parsed_url = urlparse(wiki) parsed_url = urlparse(wiki)
WIKI_API_PATH = wiki + request["query"]["general"]["scriptpath"] + "/api.php" WIKI_API_PATH = wiki + request["query"]["general"]["scriptpath"] + "api.php"
WIKI_SCRIPT_PATH = wiki WIKI_SCRIPT_PATH = wiki
WIKI_ARTICLE_PATH = urlunparse((*parsed_url[0:2], "", "", "", "")) + request["query"]["general"]["articlepath"] WIKI_ARTICLE_PATH = urlunparse((*parsed_url[0:2], "", "", "", "")) + request["query"]["general"]["articlepath"]
WIKI_JUST_DOMAIN = urlunparse((*parsed_url[0:2], "", "", "", "")) WIKI_JUST_DOMAIN = urlunparse((*parsed_url[0:2], "", "", "", ""))
@ -140,7 +177,15 @@ def create_article_path(article: str, WIKI_ARTICLE_PATH: str) -> str:
return WIKI_ARTICLE_PATH.replace("$1", article) return WIKI_ARTICLE_PATH.replace("$1", article)
def profile_field_name(name, embed): def profile_field_name(name, embed, _):
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")}
try: try:
return profile_fields[name] return profile_fields[name]
except KeyError: except KeyError:
@ -151,14 +196,17 @@ def profile_field_name(name, embed):
class ContentParser(HTMLParser): class ContentParser(HTMLParser):
more = _("\n__And more__")
current_tag = "" current_tag = ""
small_prev_ins = "" small_prev_ins = ""
small_prev_del = "" small_prev_del = ""
ins_length = len(more)
del_length = len(more)
added = False added = False
def __init__(self, _):
super().__init__()
self.more = _("\n__And more__")
self.ins_length = len(self.more)
self.del_length = len(self.more)
def handle_starttag(self, tagname, attribs): def handle_starttag(self, tagname, attribs):
if tagname == "ins" or tagname == "del": if tagname == "ins" or tagname == "del":
self.current_tag = tagname self.current_tag = tagname

View file

@ -1,10 +1,13 @@
import asyncio, logging import asyncio, logging, aiohttp
from src.misc import send_to_discord_webhook
from src.config import settings
logger = logging.getLogger("rcgcdw.msgqueue") logger = logging.getLogger("rcgcdw.msgqueue")
class MessageQueue: class MessageQueue:
"""Message queue class for undelivered messages""" """Message queue class for undelivered messages"""
def __init__(self): def __init__(self):
self._queue = [] self._queue = []
self.session = None
def __repr__(self): def __repr__(self):
return self._queue return self._queue
@ -24,7 +27,12 @@ class MessageQueue:
def cut_messages(self, item_num): def cut_messages(self, item_num):
self._queue = self._queue[item_num:] self._queue = self._queue[item_num:]
async def create_session(self):
self.session = aiohttp.ClientSession(headers=settings["header"], timeout=aiohttp.ClientTimeout(5.0))
async def resend_msgs(self): async def resend_msgs(self):
if self.session is None:
await self.create_session()
if self._queue: if self._queue:
logger.info( logger.info(
"{} messages waiting to be delivered to Discord due to Discord throwing errors/no connection to Discord servers.".format( "{} messages waiting to be delivered to Discord due to Discord throwing errors/no connection to Discord servers.".format(
@ -33,9 +41,9 @@ class MessageQueue:
logger.debug( logger.debug(
"Trying to send a message to Discord from the queue with id of {} and content {}".format(str(num), "Trying to send a message to Discord from the queue with id of {} and content {}".format(str(num),
str(item))) str(item)))
if send_to_discord_webhook(item) < 2: if await send_to_discord_webhook(item, self.session) < 2:
logger.debug("Sending message succeeded") logger.debug("Sending message succeeded")
await asyncio.sleep(2.5) await asyncio.sleep(1.5)
else: else:
logger.debug("Sending message failed") logger.debug("Sending message failed")
break break

View file

@ -1,4 +1,5 @@
import logging import logging
from src.database import db_cursor, db_connection
logger = logging.getLogger("rcgcdb.queue_handler") logger = logging.getLogger("rcgcdb.queue_handler")
@ -14,6 +15,9 @@ class UpdateDB():
def update_db(self): def update_db(self):
for update in self.updated: for update in self.updated:
db_cursor.execute("UPDATE rcgcdw SET rcid = ? WHERE wiki = ?", update[1], update[0])
db_connection.commit()
self.clear_list()
DBHandler = UpdateDB() DBHandler = UpdateDB()

View file

@ -1,4 +1,8 @@
import aiohttp import aiohttp
from src.config import settings from src.config import settings
session = None
session = aiohttp.ClientSession(headers=settings["header"], timeout=aiohttp.ClientTimeout(5.0))
async def start_session():
global session
session = aiohttp.ClientSession(headers=settings["header"], timeout=aiohttp.ClientTimeout(5.0))

View file

@ -1,26 +1,32 @@
from dataclasses import dataclass from dataclasses import dataclass
from src.session import session
import re import re
import logging, aiohttp import logging, aiohttp
from src.exceptions import * from src.exceptions import *
from src.database import db_cursor, db_connection from src.database import db_cursor, db_connection
from src.formatters.rc import embed_formatter, compact_formatter from src.formatters.rc import embed_formatter, compact_formatter
from src.misc import LinkParser, RecentChangesClass from src.misc import LinkParser
from i18n import langs from src.i18n import langs
import src.discord import src.discord
from src.config import settings
from bs4 import BeautifulSoup
logger = logging.getLogger("rcgcdb.wiki") logger = logging.getLogger("rcgcdb.wiki")
supported_logs = ["protect/protect", "protect/modify", "protect/unprotect", "upload/overwrite", "upload/upload", "delete/delete", "delete/delete_redir", "delete/restore", "delete/revision", "delete/event", "import/upload", "import/interwiki", "merge/merge", "move/move", "move/move_redir", "protect/move_prot", "block/block", "block/unblock", "block/reblock", "rights/rights", "rights/autopromote", "abusefilter/modify", "abusefilter/create", "interwiki/iw_add", "interwiki/iw_edit", "interwiki/iw_delete", "curseprofile/comment-created", "curseprofile/comment-edited", "curseprofile/comment-deleted", "curseprofile/comment-purged", "curseprofile/profile-edited", "curseprofile/comment-replied", "contentmodel/change", "sprite/sprite", "sprite/sheet", "sprite/slice", "managetags/create", "managetags/delete", "managetags/activate", "managetags/deactivate", "tag/update", "cargo/createtable", "cargo/deletetable", "cargo/recreatetable", "cargo/replacetable", "upload/revert"] supported_logs = ["protect/protect", "protect/modify", "protect/unprotect", "upload/overwrite", "upload/upload", "delete/delete", "delete/delete_redir", "delete/restore", "delete/revision", "delete/event", "import/upload", "import/interwiki", "merge/merge", "move/move", "move/move_redir", "protect/move_prot", "block/block", "block/unblock", "block/reblock", "rights/rights", "rights/autopromote", "abusefilter/modify", "abusefilter/create", "interwiki/iw_add", "interwiki/iw_edit", "interwiki/iw_delete", "curseprofile/comment-created", "curseprofile/comment-edited", "curseprofile/comment-deleted", "curseprofile/comment-purged", "curseprofile/profile-edited", "curseprofile/comment-replied", "contentmodel/change", "sprite/sprite", "sprite/sheet", "sprite/slice", "managetags/create", "managetags/delete", "managetags/activate", "managetags/deactivate", "tag/update", "cargo/createtable", "cargo/deletetable", "cargo/recreatetable", "cargo/replacetable", "upload/revert"]
@dataclass @dataclass
class Wiki: class Wiki:
mw_messages: int = None mw_messages: int = None
fail_times: int = 0 # corresponding to amount of times connection with wiki failed for client reasons (400-499) fail_times: int = 0 # corresponding to amount of times connection with wiki failed for client reasons (400-499)
session: aiohttp.ClientSession = None
async def create_session(self):
self.session = aiohttp.ClientSession(headers=settings["header"], timeout=aiohttp.ClientTimeout(5.0))
async def fetch_wiki(self, extended, script_path) -> aiohttp.ClientResponse: async def fetch_wiki(self, extended, script_path) -> aiohttp.ClientResponse:
if self.session is None:
await self.create_session()
url_path = script_path + "api.php" url_path = script_path + "api.php"
amount = 20 amount = 20
if extended: if extended:
@ -38,16 +44,15 @@ class Wiki:
"rcprop": "title|redirect|timestamp|ids|loginfo|parsedcomment|sizes|flags|tags|user", "rcprop": "title|redirect|timestamp|ids|loginfo|parsedcomment|sizes|flags|tags|user",
"rclimit": amount, "rctype": "edit|new|log|external", "siprop": "namespaces|general"} "rclimit": amount, "rctype": "edit|new|log|external", "siprop": "namespaces|general"}
try: try:
response = await session.get(url_path, params=params) response = await self.session.get(url_path, params=params)
except (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError): except (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError):
logger.exception("A connection error occurred while requesting {}".format(url_path)) logger.exception("A connection error occurred while requesting {}".format(url_path))
raise WikiServerError raise WikiServerError
return response return response
@staticmethod async def safe_request(self, url):
async def safe_request(url):
try: try:
request = await session.get(url, timeout=5, allow_redirects=False) request = await self.session.get(url, timeout=5, allow_redirects=False)
request.raise_for_status() request.raise_for_status()
except (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError): except (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError):
logger.exception("Reached connection error for request on link {url}".format(url=url)) logger.exception("Reached connection error for request on link {url}".format(url=url))
@ -76,6 +81,23 @@ class Wiki:
logger.warning("{} rows affected by DELETE FROM rcgcdw WHERE wiki = {}".format(db_cursor.rowcount, wiki_id)) logger.warning("{} rows affected by DELETE FROM rcgcdw WHERE wiki = {}".format(db_cursor.rowcount, wiki_id))
db_connection.commit() db_connection.commit()
async def pull_comment(self, comment_id, WIKI_API_PATH):
try:
comment = await self.safe_request(
"{wiki}?action=comment&do=getRaw&comment_id={comment}&format=json".format(wiki=WIKI_API_PATH,
comment=comment_id)).json()[
"text"]
logger.debug("Got the following comment from the API: {}".format(comment))
except (TypeError, AttributeError):
logger.exception("Could not resolve the comment text.")
except KeyError:
logger.exception("CurseProfile extension API did not respond with a valid comment content.")
else:
if len(comment) > 1000:
comment = comment[0:1000] + ""
return comment
return ""
async def process_cats(event: dict, local_wiki: Wiki, category_msgs: dict, categorize_events: dict): async def process_cats(event: dict, local_wiki: Wiki, category_msgs: dict, categorize_events: dict):
if event["type"] == "categorize": if event["type"] == "categorize":
@ -134,7 +156,7 @@ async def process_mwmsgs(wiki_response: dict, local_wiki: Wiki, mw_msgs: dict):
mw_msgs[key] = msgs # it may be a little bit messy for sure, however I don't expect any reason to remove mw_msgs entries by one mw_msgs[key] = msgs # it may be a little bit messy for sure, however I don't expect any reason to remove mw_msgs entries by one
local_wiki.mw_messages = key local_wiki.mw_messages = key
async def essential_info(change: dict, changed_categories, local_wiki: Wiki, db_wiki: tuple, target: tuple, paths: tuple): async def essential_info(change: dict, changed_categories, local_wiki: Wiki, db_wiki: tuple, target: tuple, paths: tuple, request: dict):
"""Prepares essential information for both embed and compact message format.""" """Prepares essential information for both embed and compact message format."""
def _(string: str) -> str: def _(string: str) -> str:
"""Our own translation string to make it compatible with async""" """Our own translation string to make it compatible with async"""
@ -175,4 +197,10 @@ async def essential_info(change: dict, changed_categories, local_wiki: Wiki, db_
else: else:
logger.warning("This event is not implemented in the script. Please make an issue on the tracker attaching the following info: wiki url, time, and this information: {}".format(change)) logger.warning("This event is not implemented in the script. Please make an issue on the tracker attaching the following info: wiki url, time, and this information: {}".format(change))
return return
await appearance_mode(identification_string, change, parsed_comment, changed_categories, local_wiki, target, _, ngettext, paths) additional_data = {"namespaces": request["query"]["namespaces"], "tags": {}}
for tag in request["query"]["tags"]:
try:
additional_data["tags"][tag["name"]] = (BeautifulSoup(tag["displayname"], "lxml")).get_text()
except KeyError:
additional_data["tags"][tag["name"]] = None # Tags with no displ
await appearance_mode(identification_string, change, parsed_comment, changed_categories, local_wiki, target, _, ngettext, paths, additional_data=additional_data)