Merge branch 'testing' into 'horse-thread-support'

# Conflicts:
#   src/discord/queue.py
This commit is contained in:
MarkusRost 2022-03-11 22:47:45 +00:00
commit e457757d6b
76 changed files with 3332 additions and 223 deletions

View file

@ -1,8 +1,5 @@
image: python:3.7-alpine
include:
- template: Code-Quality.gitlab-ci.yml
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
@ -37,7 +34,7 @@ pytest:
script:
- source venv/bin/activate
- pip3.7 install -U pytest
- pytest --junitxml=report.xml
- PYTHONPATH=. pytest --junit-xml=report.xml
artifacts:
when: always
reports:

View file

@ -81,7 +81,7 @@ def embed_edit(ctx: Context, change: dict) -> DiscordMessage:
changed_content = ctx.client.make_api_request(
"?action=compare&format=json&fromrev={oldrev}&torev={diff}&topst=1&prop=diff".format(
diff=change["revid"], oldrev=change["old_revid"]), "compare", "*")
except ServerError:
except (ServerError, MediaWikiError):
changed_content = None
if changed_content:
parse_mediawiki_changes(ctx, changed_content, embed)

Binary file not shown.

View file

@ -10,8 +10,8 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2021-05-23 17:18+0000\n"
"Last-Translator: magiczocker <jan-frederik-kriete@t-online.de>\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MarkusRost <sukramxro@gmail.com>\n"
"Language-Team: German <https://translate.wikibot.de/projects/rcgcdw/"
"formatters/de/>\n"
"Language: de\n"
@ -19,7 +19,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.6\n"
"X-Generator: Weblate 4.6.2\n"
"X-Loco-Source-Locale: de_DE\n"
"Generated-By: pygettext.py 1.5\n"
"X-Loco-Parser: loco_parse_po\n"
@ -1095,7 +1095,7 @@ msgstr "Passive Sichter"
#: extensions/base/mediawiki.py:38
msgid "autopatrol"
msgstr "autopatrol"
msgstr "Automatisch kontrolliert"
#: extensions/base/mediawiki.py:38
msgid "wiki_guardian"
@ -1402,7 +1402,7 @@ msgstr[0] "Minute"
msgstr[1] "Minuten"
#: extensions/base/mediawiki.py:589
#, fuzzy, python-brace-format
#, python-brace-format
msgid "for {time_number} {time_unit}"
msgstr "für {time_number} {time_unit}"

Binary file not shown.

View file

@ -8,24 +8,24 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-11-20 09:22+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MarkusRost <sukramxro@gmail.com>\n"
"Language-Team: German <https://weblate.frisk.space/projects/rcgcdw/redaction/"
"de/>\n"
"Language-Team: German <https://translate.wikibot.de/projects/rcgcdw/"
"redaction/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "versteckt"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~versteckt~~"
#~ msgid "Removed"
#~ msgstr "Versteckt"

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2021-07-11 12:33+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: Tamara Carvallo <carvallotamara@hotmail.com>\n"
"Language-Team: Spanish <https://translate.wikibot.de/projects/rcgcdw/"
"formatters/es/>\n"
@ -139,31 +139,27 @@ msgstr ""
"{target}\""
#: extensions/base/translate.py:106
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) completed moving translation pages from *{article}* "
"to [{target}]({target_url}){comment}"
msgstr ""
"[{author}]({author_url}) movió la configuración de protección de {redirect}"
"*{article}* a [{target}]({target_url}){comment}"
"[{author}]({author_url}) movió la configuración de protección de *{article}* "
"a [{target}]({target_url}){comment}"
#: extensions/base/translate.py:121
#, python-brace-format
msgid "Encountered a problem while moving \"{article}\" to \"{target}\""
msgstr ""
msgstr "Se encontró un problema al mover \"{article}\" a \"{target}\""
#: extensions/base/translate.py:133
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) encountered a problem while moving [{article}]"
"({article_url}) to [{target}]({target_url}){comment}"
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) con [{dest}]({dest_url}){comment}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) en [{dest}]({dest_url}){comment}"
"[{author}]({author_url}) encontró un problema al mover "
"[{article}]({article_url}) a [{target}]({target_url}){comment}"
#: extensions/base/translate.py:149
#, python-brace-format
@ -171,32 +167,30 @@ msgid ""
"Failed to delete \"{article}\" which belongs to translatable page "
"\"{target}\""
msgstr ""
"No se pudo borrar \"{article}\" que pertenece a la página traducible \""
"{target}\""
#: extensions/base/translate.py:161
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) failed to delete [{article}]({article_url}) which "
"belongs to translatable page [{target}]({target_url}){comment}"
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) con [{dest}]({dest_url}){comment}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) en [{dest}]({dest_url}){comment}"
"[{author}]({author_url}) no pudo borrar [{article}]({article_url}) que "
"pertenece a la página traducible [{target}]({target_url}){comment}"
#: extensions/base/translate.py:177
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Completed deletion of translation page \"{article}\""
msgstr "Modificó la visibilidad de la revisión en la página {article} "
msgstr "Se eliminó completamente la página de traducción \"{article}\""
#: extensions/base/translate.py:188
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) completed deletion of translation page [{article}]"
"({article_url}){comment}"
msgstr ""
"[{author}]({author_url}) cambió la visibilidad de la revisión en la página "
"[{author}]({author_url}) eliminó la página de traducción "
"[{article}]({article_url}){comment}"
#: extensions/base/translate.py:203
@ -204,108 +198,94 @@ msgstr ""
msgid ""
"Failed to delete \"{article}\" which belongs to translation page \"{target}\""
msgstr ""
"No se pudo borrar \"{article}\" que pertenece a la página de traducción \""
"{target}\""
#: extensions/base/translate.py:215
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) failed to delete [{article}]({article_url}) which "
"belongs to translation page [{target}]({target_url}){comment}"
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) con [{dest}]({dest_url}){comment}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) fusionó los historiales de revisión de [{article}]"
"({article_url}) en [{dest}]({dest_url}){comment}"
"[{author}]({author_url}) no pudo borrar [{article}]({article_url}) que "
"pertenece a la página de traducción [{target}]({target_url}){comment}"
#: extensions/base/translate.py:231
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Encouraged translation of \"{article}\""
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"Quitó la protección de {article}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"Eliminó la protección de {article}"
msgstr "Traducción recomendada de \"{article}\""
#: extensions/base/translate.py:240
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) encouraged translation of [{article}]({article_url})"
"{comment}"
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) eliminó la protección de [{article}]({article_url})"
"{comment}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) quitó la protección de [{article}]({article_url})"
"{comment}"
"[{author}]({author_url}) recomendó la traducción de "
"[{article}]({article_url}){comment}"
#: extensions/base/translate.py:255
#, python-brace-format
msgid "Discouraged translation of \"{article}\""
msgstr ""
msgstr "Traducción desaconsejada de \"{article}\""
#: extensions/base/translate.py:264
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) discouraged translation of [{article}]"
"({article_url}){comment}"
msgstr "[{author}]({author_url}) restauró [{article}]({article_url}){comment}"
msgstr ""
"[{author}]({author_url}) desaconsejó la traducción de "
"[{article}]({article_url}){comment}"
#: extensions/base/translate.py:282
#, python-brace-format
msgid "Limited languages for \"{article}\" to `{languages}`"
msgstr ""
msgstr "Idiomas limitados para \"{article}\" a `{languages}`"
#: extensions/base/translate.py:285
#, python-brace-format
msgid "Priority languages for \"{article}\" set to `{languages}`"
msgstr ""
"Los idiomas prioritarios para \"{article}\" se establecieron en `{languages}`"
#: extensions/base/translate.py:288
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Removed priority languages from \"{article}\""
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"Quitó la protección de {article}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"Eliminó la protección de {article}"
msgstr "Se quitaron los idiomas prioritarios de \"{article}\""
#: extensions/base/translate.py:301
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) limited languages for [{article}]({article_url}) to "
"`{languages}`{comment}"
msgstr ""
"[{author}]({author_url}) modificó la configuración de protección de "
"[{article}]({article_url}) a: {settings}{comment}"
"[{author}]({author_url}) limitó los idiomas para [{article}]({article_url}) "
"a `{languages}`{comment}"
#: extensions/base/translate.py:308
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) set the priority languages for [{article}]"
"({article_url}) to `{languages}`{comment}"
msgstr ""
"[{author}]({author_url}) modificó la configuración de protección de "
"[{article}]({article_url}) a: {settings}{comment}"
"[{author}]({author_url}) estableció los idiomas de prioridad para "
"[{article}]({article_url}) en `{languages}`{comment}"
#: extensions/base/translate.py:315
#, fuzzy, python-brace-format
#, python-brace-format
msgid ""
"[{author}]({author_url}) removed priority languages from [{article}]"
"({article_url}){comment}"
msgstr ""
"#-#-#-#-# rcgcdw.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) eliminó la protección de [{article}]({article_url})"
"{comment}\n"
"#-#-#-#-# formatters.po (RcGcDw) #-#-#-#-#\n"
"[{author}]({author_url}) quitó la protección de [{article}]({article_url})"
"{comment}"
"[{author}]({author_url}) eliminó los idiomas prioritarios de "
"[{article}]({article_url}){comment}"
#: extensions/base/translate.py:331
#, python-brace-format
msgid "Added translatable page \"{article}\" to aggregate group \"{group}\""
msgstr ""
"Se agregó la página traducible \"{article}\" al grupo agregado \"{group}\""
#: extensions/base/translate.py:342
#, fuzzy, python-brace-format

Binary file not shown.

View file

@ -8,24 +8,25 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-11-16 22:51+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: Frisk The Evil Goat Overlord <piotrex43@protonmail.ch>\n"
"Language-Team: Polish <https://translate.wikibot.de/projects/rcgcdw/"
"redaction/pl/>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.1\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 "
"|| n%100>14) ? 1 : 2);\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "ukryte"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~ukryte~~"
#~ msgid "Removed"
#~ msgstr "Usunięte"

View file

@ -21,7 +21,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:34+0200\n"
"PO-Revision-Date: 2021-07-11 12:33+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: Eduaddad <duduaddad@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translate.wikibot.de/projects/"
"rcgcdw/formatters/pt-br/>\n"
@ -1402,7 +1402,7 @@ msgstr[0] "minuto"
msgstr[1] "minutos"
#: extensions/base/mediawiki.py:589
#, fuzzy, python-brace-format
#, python-brace-format
msgid "for {time_number} {time_unit}"
msgstr "por {time_number} {time_unit}"

View file

@ -8,24 +8,24 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-11-21 01:09+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: Eduaddad <duduaddad@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://weblate.frisk.space/projects/"
"Language-Team: Portuguese (Brazil) <https://translate.wikibot.de/projects/"
"rcgcdw/redaction/pt-br/>\n"
"Language: pt-br\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "oculto"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~oculto~~"
#~ msgid "Removed"
#~ msgstr "Removido"

Binary file not shown.

View file

@ -21,8 +21,8 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2021-07-11 12:33+0000\n"
"Last-Translator: Philo04 <philipp2012lopu@mail.ru>\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MakandIv <makushkin_andrey@mail.ru>\n"
"Language-Team: Russian <https://translate.wikibot.de/projects/rcgcdw/"
"formatters/ru/>\n"
"Language: ru\n"
@ -1420,7 +1420,7 @@ msgstr[1] "минуты"
msgstr[2] "минут"
#: extensions/base/mediawiki.py:589
#, fuzzy, python-brace-format
#, python-brace-format
msgid "for {time_number} {time_unit}"
msgstr "на {time_number} {time_unit}"

Binary file not shown.

View file

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-12-22 00:13+0000\n"
"Last-Translator: Philo04 <philipp2012lopu@mail.ru>\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MakandIv <makushkin_andrey@mail.ru>\n"
"Language-Team: Russian <https://translate.wikibot.de/projects/rcgcdw/"
"redaction/ru/>\n"
"Language: ru\n"
@ -18,15 +18,15 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "скрыто"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~скрыто~~"
#~ msgid "Removed"
#~ msgstr "Удалено"

Binary file not shown.

View file

@ -16,32 +16,22 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-11-18 07:47+0000\n"
"Last-Translator: MakandIv <>\n"
"Language-Team: Ukrainian <https://weblate.frisk.space/projects/rcgcdw/"
"rc_formatters/uk/>\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MakandIv <makushkin_andrey@mail.ru>\n"
"Language-Team: Ukrainian <https://translate.wikibot.de/projects/rcgcdw/"
"formatters/uk/>\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"#-#-#-#-# rcgcdw.po #-#-#-#-#\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
"X-Generator: Poedit 2.4.1\n"
"#-#-#-#-# formatters.po #-#-#-#-#\n"
"#-#-#-#-# discussion_formatters.po #-#-#-#-#\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 19.12.3\n"
"#-#-#-#-# rc_formatters.po #-#-#-#-#\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/api/util.py:61 src/api/util.py:66
msgid "__Only whitespace__"
@ -59,10 +49,9 @@ msgstr "Додано"
#: src/api/util.py:141 extensions/base/discussions.py:247
#: extensions/base/discussions.py:264 extensions/base/abusefilter.py:45
msgid "Unregistered user"
msgstr ""
msgstr "Незареєстрований користувач"
#: src/api/util.py:160
#, fuzzy
msgctxt "recent changes Tags"
msgid "Tags"
msgstr "Теги"
@ -88,49 +77,49 @@ msgid "Changed categories"
msgstr "Змінені категорії"
#: extensions/base/cargo.py:37
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Created the Cargo table \"{table}\""
msgstr "Створив тег \"{tag}\""
msgstr "Створив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:45
#, fuzzy, python-brace-format
#, python-brace-format
msgid "[{author}]({author_url}) created the Cargo table \"{table}\""
msgstr "[{author}]({author_url}) створив [тег]({tag_url}) \"{tag}\""
msgstr "[{author}]({author_url}) створив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:60
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Recreated the Cargo table \"{table}\""
msgstr "Створив тег \"{tag}\""
msgstr "Відтворив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:68
#, fuzzy, python-brace-format
#, python-brace-format
msgid "[{author}]({author_url}) recreated the Cargo table \"{table}\""
msgstr "[{author}]({author_url}) створив [тег]({tag_url}) \"{tag}\""
msgstr "[{author}]({author_url}) відтворив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:83
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Replaced the Cargo table \"{table}\""
msgstr "Вилучив тег \"{tag}\""
msgstr "Замінив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:91
#, fuzzy, python-brace-format
#, python-brace-format
msgid "[{author}]({author_url}) replaced the Cargo table \"{table}\""
msgstr "[{author}]({author_url}) створив [тег]({tag_url}) \"{tag}\""
msgstr "[{author}]({author_url}) замінив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:105
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Deleted the Cargo table \"{table}\""
msgstr "Вилучив тег \"{tag}\""
msgstr "Видалив таблицю Cargo \"{table}\""
#: extensions/base/cargo.py:112
#, fuzzy, python-brace-format
#, python-brace-format
msgid "[{author}]({author_url}) deleted the Cargo table \"{table}\""
msgstr "[{author}]({author_url}) вилучив [тег]({tag_url}) \"{tag}\""
msgstr "[{author}]({author_url}) видалив таблицю Cargo \"{table}\""
#: extensions/base/translate.py:41
#, python-brace-format
msgid "Marked \"{article}\" for translation"
msgstr ""
msgstr "Зазначив сторінку «{article}» як доступну для перекладу"
#: extensions/base/translate.py:55
#, fuzzy, python-brace-format

Binary file not shown.

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2021-05-22 12:08+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: MakandIv <makushkin_andrey@mail.ru>\n"
"Language-Team: Ukrainian <https://translate.wikibot.de/projects/rcgcdw/"
"redaction/uk/>\n"
@ -18,15 +18,15 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.6\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "приховано"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~приховано~~"
#~ msgid "Removed"
#~ msgstr "Видалено"

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2021-05-31 21:54+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: lakejason0 <sunliyuan200402@outlook.com>\n"
"Language-Team: Chinese (Simplified) <https://translate.wikibot.de/projects/"
"rcgcdw/formatters/zh-hans/>\n"
@ -1347,7 +1347,7 @@ msgid_plural "minutes"
msgstr[0] "分"
#: extensions/base/mediawiki.py:589
#, fuzzy, python-brace-format
#, python-brace-format
msgid "for {time_number} {time_unit}"
msgstr "时长为 {time_number} {time_unit}"

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-12-13 14:30+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: lakejason0 <sunliyuan200402@outlook.com>\n"
"Language-Team: Chinese (Simplified) <https://translate.wikibot.de/projects/"
"rcgcdw/redaction/zh-hans/>\n"
@ -17,15 +17,15 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "已隐藏"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~已隐藏~~"
#~ msgid "Removed"
#~ msgstr "移除了"

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: RcGcDw\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-11 14:32+0200\n"
"PO-Revision-Date: 2020-12-13 14:30+0000\n"
"PO-Revision-Date: 2021-07-30 10:44+0000\n"
"Last-Translator: lakejason0 <sunliyuan200402@outlook.com>\n"
"Language-Team: Chinese (Traditional) <https://translate.wikibot.de/projects/"
"rcgcdw/redaction/zh-hant/>\n"
@ -17,15 +17,15 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.2.1\n"
"X-Generator: Weblate 4.6.2\n"
#: src/discord/redaction.py:77
msgid "hidden"
msgstr ""
msgstr "已隱藏"
#: src/discord/redaction.py:80 src/discord/redaction.py:85
msgid "~~hidden~~"
msgstr ""
msgstr "~~已隱藏~~"
#~ msgid "Removed"
#~ msgstr "移除了"

View file

@ -36,6 +36,7 @@
"show_abuselog": false,
"hide_ips": false,
"discord_message_cooldown": 0,
"datafile_path": "data.json",
"auto_suppression": {
"enabled": false,
"db_location": ":memory:"

View file

@ -2,7 +2,7 @@ from setuptools import setup
setup(
name='RcGcDw',
version='1.14',
version='1.14.0.1',
packages=[''],
url='https://gitlab.com/piotrex43/RcGcDw/',
license='GNU GPLv3',

View file

@ -21,6 +21,7 @@ from typing import Optional, Callable
logger = logging.getLogger("src.api.formatter")
def _register_formatter(func, kwargs, formatter_type: str, action_type=None):
"""
Registers a formatter inside of src.rcgcdw.formatter_hooks

View file

@ -14,6 +14,10 @@
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
# Made just to avoid circular imports
formatter_hooks = {}
pre_hooks = []
post_hooks = []
from typing import Callable, List, Dict
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.api.context import Context
formatter_hooks: Dict[str, Callable[[Context, dict], DiscordMessage]] = {}
pre_hooks: List[Callable[[Context, dict], None]] = []
post_hooks: List[Callable[[DiscordMessage, DiscordMessageMetadata, Context, dict], None]] = []

22
src/argparser.py Normal file
View file

@ -0,0 +1,22 @@
# 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 <http://www.gnu.org/licenses/>.
import argparse
parser = argparse.ArgumentParser(description="Start RcGcDw")
parser.add_argument("--test", action='store_true', help="mode used for testing, sends only 5 entries of both rc and discussion changes and sends daily overview")
parser.add_argument("--settings", default="settings.json", type=argparse.FileType(encoding='utf8'), help="provides a path to settings file (default ./settings.json)")
command_args, unknown = parser.parse_known_args()

View file

@ -16,19 +16,19 @@
import json
import logging
import sys
from src.argparser import command_args
global settings
def load_settings():
global settings
try: # load settings
with open("settings.json", encoding="utf8") as sfile:
settings = json.load(sfile)
command_args.settings.seek(0)
settings = json.load(command_args.settings)
if settings["limitrefetch"] < settings["limit"] and settings["limitrefetch"] != -1:
settings["limitrefetch"] = settings["limit"]
if "user-agent" in settings["header"]:
settings["header"]["user-agent"] = settings["header"]["user-agent"].format(version="1.14") # set the version in the useragent
settings["header"]["user-agent"] = settings["header"]["user-agent"].format(version="1.14.1") # set the version in the useragent
except FileNotFoundError:
logging.critical("No config file could be found. Please make sure settings.json is in the directory.")
sys.exit(1)
@ -45,4 +45,3 @@ def load_settings():
load_settings()

View file

@ -17,12 +17,12 @@ import re
import sys
import time
import logging
from typing import Optional
from typing import Optional, Union, Tuple
import requests
from src.configloader import settings
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.discord.message import DiscordMessage, DiscordMessageMetadata, DiscordMessageRaw
AUTO_SUPPRESSION_ENABLED = settings.get("auto_suppression", {"enabled": False}).get("enabled")
if AUTO_SUPPRESSION_ENABLED:
@ -35,7 +35,7 @@ logger = logging.getLogger("rcgcdw.discord.queue")
class MessageQueue:
"""Message queue class for undelivered messages"""
def __init__(self):
self._queue = []
self._queue: list[Tuple[Union[DiscordMessage, DiscordMessageRaw], DiscordMessageMetadata]] = []
def __repr__(self):
return self._queue
@ -49,10 +49,10 @@ class MessageQueue:
def clear(self):
self._queue.clear()
def add_message(self, message):
def add_message(self, message: Tuple[Union[DiscordMessage, DiscordMessageRaw], DiscordMessageMetadata]):
self._queue.append(message)
def cut_messages(self, item_num):
def cut_messages(self, item_num: int):
self._queue = self._queue[item_num:]
@staticmethod
@ -116,6 +116,9 @@ def handle_discord_http(code, formatted_embed, result):
"Discord have trouble processing the event, and because the HTTP code returned is {} it means we blame them.".format(
code))
return 3
else:
logger.error("There was an unexpected HTTP code returned from Discord: {}".format(code))
return 1
def update_ratelimit(request):
@ -183,5 +186,5 @@ def send_to_discord(data: Optional[DiscordMessage], meta: DiscordMessageMetadata
elif code == 2:
time.sleep(5.0)
messagequeue.add_message((data, meta))
elif code < 2:
elif code is None or code < 2:
pass

View file

@ -15,8 +15,10 @@
import logging
import json
from typing import List, Union
from src.configloader import settings
from src.discord.message import DiscordMessageMetadata, DiscordMessage, DiscordMessageRaw
from src.discord.message import DiscordMessageMetadata, DiscordMessageRaw
from src.discord.queue import send_to_discord, messagequeue
from src.fileio.database import db_cursor, db_connection
from src.i18n import redaction as redaction_translation
@ -48,14 +50,14 @@ def delete_messages(matching_data: dict):
db_connection.commit()
def redact_messages(ids: list, entry_type: int, to_censor: dict):
def redact_messages(ids: Union[List[Union[str, int]], set[Union[int, str]]], entry_type: int, to_censor: dict):
"""Redact past Discord messages
ids: list of ints
entry_type: int - 0 for revdel, 1 for logdel
to_censor: dict - logparams of message parts to censor"""
for event_id in ids:
if entry_type == 0: # TODO check if queries are proper
if entry_type == 0:
message = db_cursor.execute("SELECT content, message_id FROM messages INNER JOIN event ON event.msg_id = messages.message_id WHERE event.revid = ?;", (event_id, ))
else:
message = db_cursor.execute(
@ -90,3 +92,23 @@ def redact_messages(ids: list, entry_type: int, to_censor: dict):
send_to_discord(DiscordMessageRaw(message, settings["webhookURL"].split("?", 1)[0]+"/messages/"+str(row[1])), DiscordMessageMetadata("PATCH"))
else:
logger.debug("Could not find message in the database.")
def find_middle_next(ids: List[str], pageid: int) -> set:
"""To address #235 RcGcDw should now remove diffs in next revs relative to redacted revs to protect information in revs that revert revdeleted information.
:arg ids - list
:arg pageid - int
:return list"""
ids = [int(x) for x in ids]
result = set()
ids.sort() # Just to be sure, sort the list to make sure it's always sorted
messages = db_cursor.execute("SELECT revid FROM event WHERE pageid = ? AND revid >= ? ORDER BY revid", (pageid, ids[0],))
all_in_page = [x[0] for x in messages.fetchall()]
for id in ids:
try:
result.add(all_in_page[all_in_page.index(id)+1])
except (KeyError, ValueError):
logger.debug(f"Value {id} not in {all_in_page} or no value after that.")
return result - set(ids)

View file

@ -16,7 +16,7 @@
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
import logging, schedule, requests
from typing import Dict, Any, Optional
from typing import Optional
from src.configloader import settings
@ -134,6 +134,7 @@ def parse_discussion_post(post, comment_pages):
raise
metadata = DiscordMessageMetadata("POST")
run_hooks(post_hooks, discord_message, metadata, context, post)
discord_message.finish_embed()
send_to_discord(discord_message, metadata)

View file

@ -15,12 +15,31 @@
import sqlite3
import logging
import json
from src.configloader import settings
logger = logging.getLogger("rcgcdw.fileio.database")
def catch_db_OperationalError(func):
def catcher(*args, **kwargs):
global db_connection, db_cursor
try:
func(*args, **kwargs)
except sqlite3.OperationalError:
if settings.get("error_tolerance", 0) > 1:
logger.error("SQL database has been damaged during operation. This can indicate it has been deleted "
"during runtime or damaged in some way. If it wasn't purposeful you may want to take a look "
"at your disk state. In the meantime, RcGcDw will attempt to recover by re-creating empty database.")
db_connection, db_cursor = create_connection()
check_tables()
func(*args, **kwargs)
else:
raise
return func
return catcher
def create_schema():
"""Creates a SQLite database schema"""
logger.info("Creating database schema...")
@ -60,6 +79,7 @@ def check_tables():
create_schema()
@catch_db_OperationalError
def add_entry(pageid: int, revid: int, logid: int, message, message_id: str):
"""Add an edit or log entry to the DB
:param message:
@ -69,21 +89,27 @@ def add_entry(pageid: int, revid: int, logid: int, message, message_id: str):
:param message_id:
"""
db_cursor.execute("INSERT INTO messages (message_id, content) VALUES (?, ?)", (message_id, message))
db_cursor.execute("INSERT INTO event (pageid, revid, logid, msg_id) VALUES (?, ?, ?, ?)", (pageid, revid, logid, message_id))
logger.debug("Adding an entry to the database (pageid: {}, revid: {}, logid: {}, message: {})".format(pageid, revid, logid, message))
db_cursor.execute("INSERT INTO event (pageid, revid, logid, msg_id) VALUES (?, ?, ?, ?)",
(pageid, revid, logid, message_id))
logger.debug(
"Adding an entry to the database (pageid: {}, revid: {}, logid: {}, message: {})".format(pageid, revid, logid,
message))
db_connection.commit()
@catch_db_OperationalError
def clean_entries():
"""Cleans entries that are 50+"""
cleanup = db_cursor.execute(
"SELECT message_id FROM messages WHERE message_id NOT IN (SELECT message_id FROM messages ORDER BY message_id desc LIMIT 50);")
for row in cleanup:
db_cursor.execute("DELETE FROM messages WHERE message_id = ?", (row[0],))
cleanup = db_cursor.execute("SELECT msg_id FROM event WHERE msg_id NOT IN (SELECT msg_id FROM event ORDER BY msg_id desc LIMIT 50);")
cleanup = db_cursor.execute(
"SELECT msg_id FROM event WHERE msg_id NOT IN (SELECT msg_id FROM event ORDER BY msg_id desc LIMIT 50);")
for row in cleanup:
db_cursor.execute("DELETE FROM event WHERE msg_id = ?", (row[0],))
db_connection.commit()
db_connection, db_cursor = create_connection()
check_tables()

View file

@ -14,9 +14,15 @@
# along with RcGcDw. If not, see <http://www.gnu.org/licenses/>.
import gettext, sys, logging
from typing import Union, Optional
from src.configloader import settings
logger = logging.getLogger("rcgcdw.i18n")
rcgcdw: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
discussion_formatters: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
rc: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
formatters_i18n: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
misc: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
redaction: Optional[Union[gettext.GNUTranslations, gettext.NullTranslations]] = None
# Setup translation
@ -27,6 +33,9 @@ def python37_pgettext_backward_compatibility(context: str, string: str):
return string
return translation
def load_languages():
global rcgcdw, rc, formatters_i18n, misc, redaction
try:
if settings["lang"] != "en":
rcgcdw = gettext.translation('rcgcdw', localedir='locale', languages=[settings["lang"]])
@ -40,3 +49,6 @@ try:
except FileNotFoundError:
logger.critical("No language files have been found. Make sure locale folder is located in the directory.")
sys.exit(1)
load_languages()

View file

@ -48,15 +48,15 @@ profile_fields = {"profile-location": _("Location"), "profile-aboutme": _("About
class DataFile:
"""Data class which instance of is shared by multiple modules to remain consistent and do not cause too many IO operations."""
def __init__(self):
self.data = self.load_datafile()
misc_logger.debug("Current contents of data.json {}".format(self.data))
self.changed = False
self.data_filename: str = settings.get("datafile_path", "data.json")
self.data: dict = self.load_datafile()
misc_logger.debug("Current contents of {} {}".format(self.data_filename, self.data))
self.changed: bool = False
@staticmethod
def generate_datafile():
def generate_datafile(self):
"""Generate a data.json file from a template."""
try:
with open("data.json", 'w', encoding="utf-8") as data:
with open(self.data_filename, 'w', encoding="utf-8") as data:
data.write(json.dumps(data_template, indent=4))
except PermissionError:
misc_logger.critical("Could not create a data file (no permissions). No way to store last edit.")
@ -67,7 +67,7 @@ class DataFile:
:rtype: dict
"""
try:
with open("data.json", encoding="utf-8") as data:
with open(self.data_filename, encoding="utf-8") as data:
return json.loads(data.read())
except FileNotFoundError:
self.generate_datafile()
@ -79,7 +79,7 @@ class DataFile:
if self.changed is False: # don't cause unnecessary write operations
return
try:
with open("data.json", "w", encoding="utf-8") as data_file:
with open(self.data_filename, "w", encoding="utf-8") as data_file:
data_file.write(json.dumps(self.data, indent=4))
self.changed = False
misc_logger.debug("Saving the database succeeded.")
@ -89,7 +89,7 @@ class DataFile:
except OSError as e:
if settings.get("error_tolerance", 1) > 1:
if platform.system() == "Windows":
if "Invalid argument: 'data.json'" in str(e):
if "Invalid argument: '" + self.data_filename + "'" in str(e):
misc_logger.error("Saving the data file failed due to Invalid argument exception, we've seen it "
"before in issue #209, if you know the reason for it happening please reopen the "
"issue with explanation, for now we are going to just ignore it.") # Reference #209
@ -97,6 +97,7 @@ class DataFile:
raise
def __setitem__(self, instance, value):
if self.data[instance] != value:
self.data[instance] = value
self.changed = True
@ -255,16 +256,17 @@ def add_to_dict(dictionary, key):
return dictionary
def prepare_paths(path, dry=False):
global WIKI_API_PATH
global WIKI_ARTICLE_PATH
global WIKI_SCRIPT_PATH
global WIKI_JUST_DOMAIN
def prepare_paths(path: str, dry=False):
"""Set the URL paths for article namespace and script namespace
WIKI_API_PATH will be: WIKI_DOMAIN/api.php
WIKI_ARTICLE_PATH will be: WIKI_DOMAIN/articlepath/$1 where $1 is the replaced string
WIKI_SCRIPT_PATH will be: WIKI_DOMAIN/
WIKI_JUST_DOMAIN will be: WIKI_DOMAIN"""
global WIKI_API_PATH
global WIKI_ARTICLE_PATH
global WIKI_SCRIPT_PATH
global WIKI_JUST_DOMAIN
def quick_try_url(url):
"""Quickly test if URL is the proper script path,
False if it appears invalid

View file

@ -22,29 +22,30 @@
import time, logging.config, requests, datetime, math, os.path, schedule, sys, re, importlib
import src.misc
import src.configloader
from collections import defaultdict, Counter, OrderedDict
from src.argparser import command_args
from typing import Optional
import src.api.client
from src.api.context import Context
from src.api.hooks import formatter_hooks, pre_hooks, post_hooks
from src.configloader import settings
from src.misc import add_to_dict, datafile, WIKI_API_PATH, LinkParser, run_hooks
from src.misc import add_to_dict, datafile, run_hooks
from src.api.util import create_article_path, default_message
from src.discord.queue import send_to_discord
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.exceptions import MWError, ServerError, MediaWikiError, BadRequest, ClientError, NoFormatter
from src.exceptions import ServerError, MediaWikiError, NoFormatter
from src.i18n import rcgcdw
from src.wiki import Wiki
settings = src.configloader.settings
_ = rcgcdw.gettext
ngettext = rcgcdw.ngettext
TESTING = True if "--test" in sys.argv else False # debug mode, pipeline testing
TESTING = command_args.test # debug mode, pipeline testing
AUTO_SUPPRESSION_ENABLED = settings.get("auto_suppression", {"enabled": False}).get("enabled")
if AUTO_SUPPRESSION_ENABLED:
from src.discord.redaction import delete_messages, redact_messages
from src.discord.redaction import delete_messages, redact_messages, find_middle_next
# Prepare logging
logging.config.dictConfig(settings["logging"])
@ -259,8 +260,11 @@ def rc_processor(change, changed_categories):
delete_messages(dict(logid=logid))
elif identification_string == "delete/revision" and AUTO_SUPPRESSION_ENABLED:
logparams = change.get('logparams', {"ids": []})
if logparams.get("type", "") in ("revision", "logging", "oldimage"):
if settings["appearance"]["mode"] == "embed":
redact_messages(logparams.get("ids", []), 0, logparams.get("new", {}))
if "content" in logparams.get("new", {}) and settings.get("appearance", {}).get("embed", {}).get("show_edit_changes", False): # Also redact revisions in the middle and next ones in case of content (diffs leak)
redact_messages(find_middle_next(logparams.get("ids", []), change.get("pageid", -1)), 0, {"content": ""})
else:
for revid in logparams.get("ids", []):
delete_messages(dict(revid=revid))
@ -338,6 +342,7 @@ if TESTING:
day_overview()
import src.discussions
src.discussions.fetch_discussions()
logger.info("Test has succeeded without premature exceptions.")
sys.exit(0)
while 1:

View file

@ -38,6 +38,7 @@ storage = datafile
logger = logging.getLogger("rcgcdw.rc")
class Wiki(object):
"""Store verious data and functions related to wiki and fetching of Recent Changes"""
def __init__(self, rc_processor: Callable, abuse_processor: Callable):
@ -394,8 +395,7 @@ class Wiki(object):
if self.downtimecredibility < 60:
self.downtimecredibility += 15
else:
if (
time.time() - self.last_downtime) > 1800 and self.check_connection(): # check if last downtime happened within 30 minutes, if yes, don't send a message
if (time.time() - self.last_downtime) > 1800 and self.check_connection(): # check if last downtime happened within 30 minutes, if yes, don't send a message
send_simple("down_detector", _("{wiki} seems to be down or unreachable.").format(wiki=settings["wikiname"]),
_("Connection status"), settings["avatars"]["connection_failed"])
self.last_downtime = time.time()

View file

93
test/data/rc_objects.json Normal file
View file

@ -0,0 +1,93 @@
{
"edit": {
"type": "edit",
"ns": 0,
"title": "Some page",
"pageid": 9327,
"revid": 2075232,
"old_revid": 232555,
"rcid": 2793437,
"user": "User3",
"oldlen": 32882,
"newlen": 328,
"timestamp": "2022-03-26T11:41:22Z",
"parsedcomment": "Work on new as",
"tags": ["VisualEdit"]
},
"abuselog": {
"id": 4690092,
"filter": "Prevent vandalism",
"filter_id": "3",
"user": "User4",
"ns": 0,
"title": "Survival",
"action": "edit",
"result": "tag",
"revid": "398328478",
"timestamp": "2022-01-13T06:20:58Z"
},
"cargo/createtable": {
"type": "log",
"ns": 0,
"title": "",
"pageid": 0,
"revid": 0,
"old_revid": 0,
"rcid": 13325,
"user": "User 10",
"oldlen": 0,
"newlen": 0,
"timestamp": "2022-01-13T15:25:09Z",
"parsedcomment": "",
"logid": 1865,
"logtype": "cargo",
"logaction": "createtable",
"logparams": {
"0": "<a href=\"https://somewiki.somefarm.com/wiki/Special:CargoTables/TableTest\">TableTest</a>"
},
"tags": []
},
"datadump/generate": {
"type": "log",
"ns": 0,
"title": "Datadump",
"pageid": 0,
"revid": 0,
"old_revid": 0,
"rcid": 13325,
"user": "User 10",
"timestamp": "2022-01-13T15:25:09Z",
"parsedcomment": "",
"logid": 18,
"logtype": "datadump",
"logaction": "generate",
"logparams": {
"filename": "Somefilenoidea.tar.gz"
},
"tags": []
},
"interwiki/iw_add": {
"type": "log",
"ns": -1,
"title": "Special:Interwiki",
"pageid": 0,
"revid": 0,
"old_revid": 0,
"rcid": 13326,
"user": "Not An User",
"oldlen": 0,
"newlen": 0,
"timestamp": "2022-01-13T15:31:25Z",
"parsedcomment": "Just a test",
"logid": 1866,
"logtype": "interwiki",
"logaction": "iw_add",
"logparams": {
"0": "testonlypleaseignore",
"1": "https://notawiki.notaplatform.com/wiki/$1",
"2": "0",
"3": "0"
},
"tags": []
}
}

View file

@ -0,0 +1,7 @@
{
"edit": {"allowed_mentions": {"parse": []}, "avatar_url": "", "embeds": [{"color": 16711680, "url": "https://minecraft.fandom.com/index.php?title=Some_page&curid=9327&diff=2075232&oldid=232555", "title": "Some page (-32554)", "fields": [{"name": "Removed", "value": "__Only whitespace__", "inline": true}, {"name": "Added", "value": "__Only whitespace__", "inline": true}, {"name": "Tagi", "value": "VisualEdit", "inline": false}], "author": {"name": "User3", "url": "https://minecraft.fandom.com/wiki/User:User3", "icon_url": ""}, "timestamp": "2022-03-26T11:41:22Z", "description": "Work on new as"}]},
"datadump/generate": {"allowed_mentions": {"parse": []}, "avatar_url": "", "embeds": [{"color": null, "author": {"name": "User 10", "url": "https://minecraft.fandom.com/wiki/User:User_10", "icon_url": ""}, "timestamp": "2022-01-13T15:25:09Z", "description": "", "title": "Generated Somefilenoidea.tar.gz dump", "url": "https://minecraft.fandom.com/wiki/Datadump"}]},
"cargo/createtable":{"allowed_mentions": {"parse": []}, "avatar_url": "", "embeds": [{"color": null, "author": {"name": "User 10", "url": "https://minecraft.fandom.com/wiki/User:User_10", "icon_url": ""}, "timestamp": "2022-01-13T15:25:09Z", "description": "", "url": "https://somewiki.somefarm.com/wiki/Special:CargoTables/TableTest", "title": "Created the Cargo table \"TableTest\""}]},
"abuselog": {"allowed_mentions": {"parse": []}, "avatar_url": "", "embeds": [{"color": null, "title": "User4 triggered \"Prevent vandalism\"", "fields": [{"name": "Performed", "value": "Edit", "inline": false}, {"name": "Action taken", "value": "Tagged the edit", "inline": false}, {"name": "Title", "value": "Survival", "inline": false}]}]},
"interwiki/iw_add": {"allowed_mentions": {"parse": []}, "avatar_url": "", "embeds": [{"color": null, "author": {"name": "Not An User", "url": "https://minecraft.fandom.com/wiki/User:Not_An_User", "icon_url": ""}, "timestamp": "2022-01-13T15:31:25Z", "url": "https://minecraft.fandom.com/wiki/Special:Interwiki", "title": "Added an entry to the interwiki table", "description": "Prefix: testonlypleaseignore, website: https://notawiki.notaplatform.com/wiki/$1 | "}]}
}

View file

@ -0,0 +1,557 @@
{
"cooldown": 30,
"wiki_url": "http://localhost:8080/",
"rc_enabled": true,
"lang": "en",
"header": {
"user-agent": "RcGcDw/{version}"
},
"limit": 15,
"webhookURL": "http://localhost:8080/webhook/",
"limitrefetch": 15,
"wikiname": "Testing Connection",
"avatars": {
"connection_failed": "https://i.imgur.com/2jWQEt1.png",
"connection_restored": "",
"no_event": "",
"embed": "",
"compact": ""
},
"ignored": ["external", "newusers/create", "newusers/autocreate", "newusers/create2", "newusers/byemail", "newusers/newusers"],
"show_updown_messages": true,
"ignored_namespaces": [],
"extensions_dir": "extensions",
"error_tolerance": 0,
"overview": false,
"overview_time": "00:00",
"send_empty_overview": false,
"license_detection": true,
"license_regex_detect": "\\{\\{(license|lizenz|licence|copyright)",
"license_regex": "\\{\\{(license|lizenz|licence|copyright)(\\ |\\|)(?P<license>.*?)\\}\\}",
"disallow_regexes": [],
"wiki_bot_login": "",
"wiki_bot_password": "",
"show_added_categories": true,
"show_bots": false,
"show_abuselog": false,
"hide_ips": false,
"discord_message_cooldown": 0,
"auto_suppression": {
"enabled": true,
"db_location": ":memory:"
},
"logging": {
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"standard": {
"format": "%(name)s - %(asctime)s - %(levelname)s: %(message)s"
}
},
"handlers": {
"default": {
"formatter": "standard",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"filelogger": {
"formatter": "standard",
"class": "logging.FileHandler",
"filename": "rcgcdw.log",
"encoding": "utf-8"
}
},
"loggers": {
"": {
"level": 0,
"handlers": ["default", "filelogger"]
}
}
},
"appearance":{
"mode": "embed",
"embed": {
"show_edit_changes": true,
"show_footer": true,
"embed_images": true,
"show_no_description_provided": true
}
},
"fandom_discussions": {
"enabled": false,
"wiki_url": "",
"cooldown": 60,
"webhookURL": "",
"limit": 5,
"appearance": {
"mode": "embed",
"embed": {
"show_content": true
}
},
"show_forums": []
},
"event_appearance": {
"daily_overview": {
"icon": "",
"color": 16312092,
"emoji": "",
"plot": true
},
"new": {
"icon": "https://i.imgur.com/6HIbEq8.png",
"color": "THIS COLOR DEPENDS ON EDIT SIZE, PLEASE DON'T CHANGE",
"emoji": "🆕"
},
"edit": {
"icon": "https://i.imgur.com/zKYHkJm.png",
"color": "THIS COLOR DEPENDS ON EDIT SIZE, PLEASE DON'T CHANGE",
"emoji": "📝"
},
"upload/overwrite": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "🖼️"
},
"upload/upload": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "🖼️"
},
"upload/revert": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "⏮️"
},
"delete/delete": {
"icon": "https://i.imgur.com/BU77GD3.png",
"color": 1,
"emoji": "🗑️"
},
"delete/delete_redir": {
"icon": "https://i.imgur.com/BU77GD3.png",
"color": 1,
"emoji": "🗑️"
},
"delete/restore": {
"icon": "https://i.imgur.com/9MnROIU.png",
"color": 1,
"emoji": "♻️"
},
"delete/revision": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"delete/event": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"merge/merge": {
"icon": "https://i.imgur.com/uQMK9XK.png",
"color": 25600,
"emoji": "🖇️"
},
"move/move": {
"icon": "https://i.imgur.com/eXz9dog.png",
"color": 25600,
"emoji": "📨"
},
"move/move_redir": {
"icon": "https://i.imgur.com/UtC3YX2.png",
"color": 25600,
"emoji": "📨"
},
"block/block": {
"icon": "https://i.imgur.com/g7KgZHf.png",
"color": 1,
"emoji": "🚫"
},
"block/unblock": {
"icon": "https://i.imgur.com/bvtBJ8o.png",
"color": 1,
"emoji": "✅"
},
"block/reblock": {
"icon": "https://i.imgur.com/g7KgZHf.png",
"color": 1,
"emoji": "🚫"
},
"protect/protect": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔒"
},
"protect/modify": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔐"
},
"protect/move_prot": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔏"
},
"protect/unprotect": {
"icon": "https://i.imgur.com/2wN3Qcq.png",
"color": 16312092,
"emoji": "🔓"
},
"import/upload": {
"icon": "",
"color": 65280,
"emoji": "📥"
},
"import/interwiki": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 65280,
"emoji": "📥"
},
"rights/rights": {
"icon": "",
"color": 16711680,
"emoji": "🏅"
},
"rights/autopromote": {
"icon": "",
"color": 16711680,
"emoji": "🏅"
},
"abusefilter/modify": {
"icon": "https://i.imgur.com/Sn2NzRJ.png",
"color": 16711680,
"emoji": "🔍"
},
"abusefilter/create": {
"icon": "https://i.imgur.com/Sn2NzRJ.png",
"color": 16711680,
"emoji": "🔍"
},
"interwiki/iw_add": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"interwiki/iw_edit": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"interwiki/iw_delete": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"curseprofile/comment-created": {
"icon": "https://i.imgur.com/Lvy5E32.png",
"color": 16089376,
"emoji": "✉️"
},
"curseprofile/comment-edited": {
"icon": "https://i.imgur.com/Lvy5E32.png",
"color": 16089376,
"emoji": "📧"
},
"curseprofile/comment-deleted": {
"icon": "",
"color": 16089376,
"emoji": "🗑️"
},
"curseprofile/comment-purged":{
"icon":"",
"color": 16089376,
"emoji": "👁️"
},
"curseprofile/comment-replied": {
"icon": "https://i.imgur.com/hkyYsI1.png",
"color": 16089376,
"emoji": "📩"
},
"curseprofile/profile-edited": {
"icon": "",
"color": 16089376,
"emoji": "📌"
},
"contentmodel/change": {
"icon": "",
"color": 25600,
"emoji": "📋"
},
"contentmodel/new": {
"icon": "",
"color": 25600,
"emoji": "📋"
},
"cargo/deletetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/createtable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/replacetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/recreatetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"sprite/sprite": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"sprite/sheet": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"sprite/slice": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"managetags/create": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/delete": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/activate": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/deactivate": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"newusers/autocreate": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/byemail": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/create": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/create2": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/newusers": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"managewiki/delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"managewiki/lock": {
"icon": "",
"color": 8421504,
"emoji": "🔒"
},
"managewiki/namespaces": {
"icon": "",
"color": 8421504,
"emoji": "📦"
},
"managewiki/namespaces-delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"managewiki/rights": {
"icon": "",
"color": 8421504,
"emoji": "🏅"
},
"managewiki/settings": {
"icon": "",
"color": 8421504,
"emoji": "⚙️"
},
"managewiki/undelete": {
"icon": "",
"color": 8421504,
"emoji": "♻️"
},
"managewiki/unlock": {
"icon": "",
"color": 8421504,
"emoji": "🔓"
},
"datadump/generate": {
"icon": "",
"color": 8421504,
"emoji": "📤"
},
"datadump/delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"pagetranslation/mark": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/unmark": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/moveok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/movenok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletefok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletefnok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletelok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletelnok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/encourage": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/discourage": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/prioritylanguages": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/associate": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/dissociate": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"translationreview/message": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"translationreview/group": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagelang/pagelang": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"renameuser/renameuser": {
"icon": "",
"color": 8421504,
"emoji": "📛"
},
"suppressed": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"discussion": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/poll": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/quiz": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/wall/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 3752525,
"emoji": "✉️"
},
"discussion/wall/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 3752525,
"emoji": "📩"
},
"discussion/comment/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 10802,
"emoji": "🗒️"
},
"discussion/comment/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 10802,
"emoji": "🗒️"
},
"unknown": {
"icon": "",
"color": 0,
"emoji": "❓"
}
}
}

View file

@ -0,0 +1,557 @@
{
"cooldown": 30,
"wiki_url": "http://localhost:8080/",
"rc_enabled": true,
"lang": "en",
"header": {
"user-agent": "RcGcDw/{version}"
},
"limit": 15,
"webhookURL": "http://localhost:8080/webhook/",
"limitrefetch": 15,
"wikiname": "Testing Connection",
"avatars": {
"connection_failed": "https://i.imgur.com/2jWQEt1.png",
"connection_restored": "",
"no_event": "",
"embed": "",
"compact": ""
},
"ignored": ["external", "newusers/create", "newusers/autocreate", "newusers/create2", "newusers/byemail", "newusers/newusers"],
"show_updown_messages": true,
"ignored_namespaces": [],
"extensions_dir": "extensions",
"error_tolerance": 2,
"overview": true,
"overview_time": "00:00",
"send_empty_overview": true,
"license_detection": false,
"license_regex_detect": "\\{\\{(license|lizenz|licence|copyright)",
"license_regex": "\\{\\{(license|lizenz|licence|copyright)(\\ |\\|)(?P<license>.*?)\\}\\}",
"disallow_regexes": ["{{version nav"],
"wiki_bot_login": "",
"wiki_bot_password": "",
"show_added_categories": false,
"show_bots": true,
"show_abuselog": false,
"hide_ips": true,
"discord_message_cooldown": 0,
"auto_suppression": {
"enabled": false,
"db_location": ":memory:"
},
"logging": {
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"standard": {
"format": "%(name)s - %(asctime)s - %(levelname)s: %(message)s"
}
},
"handlers": {
"default": {
"formatter": "standard",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"filelogger": {
"formatter": "standard",
"class": "logging.FileHandler",
"filename": "rcgcdw.log",
"encoding": "utf-8"
}
},
"loggers": {
"": {
"level": 0,
"handlers": ["default", "filelogger"]
}
}
},
"appearance":{
"mode": "compact",
"embed": {
"show_edit_changes": true,
"show_footer": true,
"embed_images": true,
"show_no_description_provided": true
}
},
"fandom_discussions": {
"enabled": false,
"wiki_url": "",
"cooldown": 60,
"webhookURL": "",
"limit": 5,
"appearance": {
"mode": "embed",
"embed": {
"show_content": true
}
},
"show_forums": []
},
"event_appearance": {
"daily_overview": {
"icon": "",
"color": 16312092,
"emoji": "",
"plot": true
},
"new": {
"icon": "https://i.imgur.com/6HIbEq8.png",
"color": "THIS COLOR DEPENDS ON EDIT SIZE, PLEASE DON'T CHANGE",
"emoji": "🆕"
},
"edit": {
"icon": "https://i.imgur.com/zKYHkJm.png",
"color": "THIS COLOR DEPENDS ON EDIT SIZE, PLEASE DON'T CHANGE",
"emoji": "📝"
},
"upload/overwrite": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "🖼️"
},
"upload/upload": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "🖼️"
},
"upload/revert": {
"icon": "https://i.imgur.com/egJpa81.png",
"color": 12390624,
"emoji": "⏮️"
},
"delete/delete": {
"icon": "https://i.imgur.com/BU77GD3.png",
"color": 1,
"emoji": "🗑️"
},
"delete/delete_redir": {
"icon": "https://i.imgur.com/BU77GD3.png",
"color": 1,
"emoji": "🗑️"
},
"delete/restore": {
"icon": "https://i.imgur.com/9MnROIU.png",
"color": 1,
"emoji": "♻️"
},
"delete/revision": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"delete/event": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"merge/merge": {
"icon": "https://i.imgur.com/uQMK9XK.png",
"color": 25600,
"emoji": "🖇️"
},
"move/move": {
"icon": "https://i.imgur.com/eXz9dog.png",
"color": 25600,
"emoji": "📨"
},
"move/move_redir": {
"icon": "https://i.imgur.com/UtC3YX2.png",
"color": 25600,
"emoji": "📨"
},
"block/block": {
"icon": "https://i.imgur.com/g7KgZHf.png",
"color": 1,
"emoji": "🚫"
},
"block/unblock": {
"icon": "https://i.imgur.com/bvtBJ8o.png",
"color": 1,
"emoji": "✅"
},
"block/reblock": {
"icon": "https://i.imgur.com/g7KgZHf.png",
"color": 1,
"emoji": "🚫"
},
"protect/protect": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔒"
},
"protect/modify": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔐"
},
"protect/move_prot": {
"icon": "https://i.imgur.com/bzPt89Z.png",
"color": 16312092,
"emoji": "🔏"
},
"protect/unprotect": {
"icon": "https://i.imgur.com/2wN3Qcq.png",
"color": 16312092,
"emoji": "🔓"
},
"import/upload": {
"icon": "",
"color": 65280,
"emoji": "📥"
},
"import/interwiki": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 65280,
"emoji": "📥"
},
"rights/rights": {
"icon": "",
"color": 16711680,
"emoji": "🏅"
},
"rights/autopromote": {
"icon": "",
"color": 16711680,
"emoji": "🏅"
},
"abusefilter/modify": {
"icon": "https://i.imgur.com/Sn2NzRJ.png",
"color": 16711680,
"emoji": "🔍"
},
"abusefilter/create": {
"icon": "https://i.imgur.com/Sn2NzRJ.png",
"color": 16711680,
"emoji": "🔍"
},
"interwiki/iw_add": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"interwiki/iw_edit": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"interwiki/iw_delete": {
"icon": "https://i.imgur.com/sFkhghb.png",
"color": 16711680,
"emoji": "🔗"
},
"curseprofile/comment-created": {
"icon": "https://i.imgur.com/Lvy5E32.png",
"color": 16089376,
"emoji": "✉️"
},
"curseprofile/comment-edited": {
"icon": "https://i.imgur.com/Lvy5E32.png",
"color": 16089376,
"emoji": "📧"
},
"curseprofile/comment-deleted": {
"icon": "",
"color": 16089376,
"emoji": "🗑️"
},
"curseprofile/comment-purged":{
"icon":"",
"color": 16089376,
"emoji": "👁️"
},
"curseprofile/comment-replied": {
"icon": "https://i.imgur.com/hkyYsI1.png",
"color": 16089376,
"emoji": "📩"
},
"curseprofile/profile-edited": {
"icon": "",
"color": 16089376,
"emoji": "📌"
},
"contentmodel/change": {
"icon": "",
"color": 25600,
"emoji": "📋"
},
"contentmodel/new": {
"icon": "",
"color": 25600,
"emoji": "📋"
},
"cargo/deletetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/createtable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/replacetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"cargo/recreatetable": {
"icon": "",
"color": 16776960,
"emoji": "📦"
},
"sprite/sprite": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"sprite/sheet": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"sprite/slice": {
"icon": "",
"color": 16776960,
"emoji": "🪟"
},
"managetags/create": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/delete": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/activate": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"managetags/deactivate": {
"icon": "",
"color": 16776960,
"emoji": "🏷️"
},
"newusers/autocreate": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/byemail": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/create": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/create2": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"newusers/newusers": {
"icon": "",
"color": 65280,
"emoji": "🗿"
},
"managewiki/delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"managewiki/lock": {
"icon": "",
"color": 8421504,
"emoji": "🔒"
},
"managewiki/namespaces": {
"icon": "",
"color": 8421504,
"emoji": "📦"
},
"managewiki/namespaces-delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"managewiki/rights": {
"icon": "",
"color": 8421504,
"emoji": "🏅"
},
"managewiki/settings": {
"icon": "",
"color": 8421504,
"emoji": "⚙️"
},
"managewiki/undelete": {
"icon": "",
"color": 8421504,
"emoji": "♻️"
},
"managewiki/unlock": {
"icon": "",
"color": 8421504,
"emoji": "🔓"
},
"datadump/generate": {
"icon": "",
"color": 8421504,
"emoji": "📤"
},
"datadump/delete": {
"icon": "",
"color": 8421504,
"emoji": "🗑️"
},
"pagetranslation/mark": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/unmark": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/moveok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/movenok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletefok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletefnok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletelok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/deletelnok": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/encourage": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/discourage": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/prioritylanguages": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/associate": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagetranslation/dissociate": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"translationreview/message": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"translationreview/group": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"pagelang/pagelang": {
"icon": "",
"color": 8421504,
"emoji": "🌐"
},
"renameuser/renameuser": {
"icon": "",
"color": 8421504,
"emoji": "📛"
},
"suppressed": {
"icon": "https://i.imgur.com/1gps6EZ.png",
"color": 1,
"emoji": "👁️"
},
"discussion": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/poll": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/forum/quiz": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 54998,
"emoji": "📝"
},
"discussion/wall/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 3752525,
"emoji": "✉️"
},
"discussion/wall/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 3752525,
"emoji": "📩"
},
"discussion/comment/post": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 10802,
"emoji": "🗒️"
},
"discussion/comment/reply": {
"icon": "https://static.wikia.nocookie.net/663e53f7-1e79-4906-95a7-2c1df4ebbada",
"color": 10802,
"emoji": "🗒️"
},
"unknown": {
"icon": "",
"color": 0,
"emoji": "❓"
}
}
}

View file

@ -0,0 +1,5 @@
{
"compare": {
"*": "<tr>\n <td colspan=\"2\" class=\"diff-lineno\">Line 8:</td>\n <td colspan=\"2\" class=\"diff-lineno\">Line 8:</td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| grasscolor = {{color|#8EB971}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| grasscolor = {{color|#8EB971}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| foliagecolor = {{color|#71A74D}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| foliagecolor = {{color|#71A74D}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n</tr>\n<tr>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n <td class=\"diff-marker\">+</td>\n <td class=\"diff-addedline\"><div>| waterc</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\"></td>\n <td class=\"diff-deletedline\"><div>| watercolor = {{color|#3F76E4}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n</tr>\n<tr>\n <td class=\"diff-marker\"></td>\n <td class=\"diff-deletedline\"><div><del class=\"diffchange diffchange-inline\">| structures ={{EnvLink|Dungeon</del>}}<del class=\"diffchange diffchange-inline\">s</del>&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">EnvLink</del>|<del class=\"diffchange diffchange-inline\">Mineshaft</del>}}<del class=\"diffchange diffchange-inline\">s</del>&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">EnvLink</del>|<del class=\"diffchange diffchange-inline\">Stronghold</del>}}<del class=\"diffchange diffchange-inline\">s</del>&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">EnvLink</del>|<del class=\"diffchange diffchange-inline\">Amethyst</del> <del class=\"diffchange diffchange-inline\">Geode</del>}}<del class=\"diffchange diffchange-inline\">s</del></div></td>\n <td class=\"diff-marker\">+</td>\n <td class=\"diff-addedline\"><div><ins class=\"diffchange diffchange-inline\">Dripleaf</ins>}}&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">BlockLink</ins>|<ins class=\"diffchange diffchange-inline\">Cave Vines</ins>}}&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">BlockLink</ins>|<ins class=\"diffchange diffchange-inline\">Spore Blossom</ins>}}&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">ItemLink</ins>|<ins class=\"diffchange diffchange-inline\">Glow</ins> <ins class=\"diffchange diffchange-inline\">Berries}}</ins>}}</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\"></td>\n <td class=\"diff-deletedline\"><div>| blocks = {{BlockLink|Oak Log}}&lt;br&gt;{{BlockLink|Azalea Leaves}}&lt;br&gt;{{BlockLink|Flowering Azalea Leaves}}&lt;br&gt;{{BlockLink|Azalea}}&lt;br&gt;{{BlockLink|Flowering Azalea}}&lt;br&gt;{{BlockLink|Rooted Dirt}}&lt;br&gt;{{BlockLink|Hanging Roots}}&lt;br&gt;{{BlockLink|Moss Block}}&lt;br&gt;{{BlockLink|Moss Carpet}}&lt;br&gt;{{BlockLink|Grass}}&lt;br&gt;{{BlockLink|Tall Grass}}&lt;br&gt;{{BlockLink|Vines}}&lt;br&gt;{{BlockLink|Water}}&lt;br&gt;{{BlockLink|Clay}}&lt;br&gt;{{BlockLink|Small Dripleaf}}&lt;br&gt;{{BlockLink|Big Dripleaf}}&lt;br&gt;{{BlockLink|Cave Vines}}&lt;br&gt;{{BlockLink|Spore Blossom}}&lt;br&gt;{{ItemLink|Glow Berries}}}}</div></td>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"></td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>A '''lush cave''' is a temperate [[Overworld]] [[cave]] [[biome]] that has a unique fauna and flora and is found underground below [[azalea tree]]s.</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>A '''lush cave''' is a temperate [[Overworld]] [[cave]] [[biome]] that has a unique fauna and flora and is found underground below [[azalea tree]]s.</div></td>\n</tr>\n\n<!-- diff cache key prod:minecraft_gamepedia:diff:wikidiff2:1.12:old-2075454:rev-2076789:1.11.0:0 -->\n"
}
}

View file

@ -0,0 +1,5 @@
{
"compare": {
"*": "<tr>\n <td colspan=\"2\" class=\"diff-lineno\">Line 8:</td>\n <td colspan=\"2\" class=\"diff-lineno\">Line 8:</td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| grasscolor = {{color|#8EB971}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| grasscolor = {{color|#8EB971}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| foliagecolor = {{color|#71A74D}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>| foliagecolor = {{color|#71A74D}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n</tr>\n<tr>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n <td class=\"diff-marker\">+</td>\n <td class=\"diff-addedline\"><div>| watercolor = {{color|#3F76E4}}{{only|java|short=y}}&lt;br&gt;{{Check the code}}{{only|bedrock|short=y}}</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\"></td>\n <td class=\"diff-deletedline\"><div>| waterc</div></td>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n</tr>\n<tr>\n <td class=\"diff-marker\"></td>\n <td class=\"diff-deletedline\"><div><del class=\"diffchange diffchange-inline\">Dripleaf</del>}}&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">BlockLink</del>|<del class=\"diffchange diffchange-inline\">Cave Vines</del>}}&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">BlockLink</del>|<del class=\"diffchange diffchange-inline\">Spore Blossom</del>}}&lt;br&gt;{{<del class=\"diffchange diffchange-inline\">ItemLink</del>|<del class=\"diffchange diffchange-inline\">Glow</del> <del class=\"diffchange diffchange-inline\">Berries}}</del>}}</div></td>\n <td class=\"diff-marker\">+</td>\n <td class=\"diff-addedline\"><div><ins class=\"diffchange diffchange-inline\">| structures ={{EnvLink|Dungeon</ins>}}<ins class=\"diffchange diffchange-inline\">s</ins>&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">EnvLink</ins>|<ins class=\"diffchange diffchange-inline\">Mineshaft</ins>}}<ins class=\"diffchange diffchange-inline\">s</ins>&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">EnvLink</ins>|<ins class=\"diffchange diffchange-inline\">Stronghold</ins>}}<ins class=\"diffchange diffchange-inline\">s</ins>&lt;br&gt;{{<ins class=\"diffchange diffchange-inline\">EnvLink</ins>|<ins class=\"diffchange diffchange-inline\">Amethyst</ins> <ins class=\"diffchange diffchange-inline\">Geode</ins>}}<ins class=\"diffchange diffchange-inline\">s</ins></div></td>\n</tr>\n<tr>\n <td colspan=\"2\" class=\"diff-empty\">&#160;</td>\n <td class=\"diff-marker\">+</td>\n <td class=\"diff-addedline\"><div>| blocks = {{BlockLink|Oak Log}}&lt;br&gt;{{BlockLink|Azalea Leaves}}&lt;br&gt;{{BlockLink|Flowering Azalea Leaves}}&lt;br&gt;{{BlockLink|Azalea}}&lt;br&gt;{{BlockLink|Flowering Azalea}}&lt;br&gt;{{BlockLink|Rooted Dirt}}&lt;br&gt;{{BlockLink|Hanging Roots}}&lt;br&gt;{{BlockLink|Moss Block}}&lt;br&gt;{{BlockLink|Moss Carpet}}&lt;br&gt;{{BlockLink|Grass}}&lt;br&gt;{{BlockLink|Tall Grass}}&lt;br&gt;{{BlockLink|Vines}}&lt;br&gt;{{BlockLink|Water}}&lt;br&gt;{{BlockLink|Clay}}&lt;br&gt;{{BlockLink|Small Dripleaf}}&lt;br&gt;{{BlockLink|Big Dripleaf}}&lt;br&gt;{{BlockLink|Cave Vines}}&lt;br&gt;{{BlockLink|Spore Blossom}}&lt;br&gt;{{ItemLink|Glow Berries}}}}</div></td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"></td>\n</tr>\n<tr>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>A '''lush cave''' is a temperate [[Overworld]] [[cave]] [[biome]] that has a unique fauna and flora and is found underground below [[azalea tree]]s.</div></td>\n <td class=\"diff-marker\">&#160;</td>\n <td class=\"diff-context\"><div>A '''lush cave''' is a temperate [[Overworld]] [[cave]] [[biome]] that has a unique fauna and flora and is found underground below [[azalea tree]]s.</div></td>\n</tr>\n\n<!-- diff cache key prod:minecraft_gamepedia:diff:wikidiff2:1.12:old-2076789:rev-2076791:1.11.0:0 -->\n"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
{
"errors": [
{
"code": "unknown_action",
"key": "apierror-unrecognizedvalue",
"params": [
"action",
"ddd"
],
"module": "main"
}
],
"*": "See https://localhost/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."
}

View file

@ -0,0 +1,32 @@
{
"batchcomplete": "",
"query": {
"pages": {
"181124": {
"pageid": 181124,
"ns": 6,
"title": "File:Oak Sign (9).png",
"imagerepository": "local",
"imageinfo": [
{
"timestamp": "2021-12-26T12:09:04Z",
"url": "http://localhost:8080/test_wiki/images/7/75/Oak_Sign_%289%29.png/revision/latest?cb=20211226120904",
"descriptionurl": "http://localhost:8080/wiki/File:Oak_Sign_(9).png",
"descriptionshorturl": "http://localhost:8080/index.php?curid=181124"
}
],
"revisions": [
{
"slots": {
"main": {
"contentmodel": "wikitext",
"contentformat": "text/x-wiki",
"*": "\n== License ==\n{{License Mojang}}"
}
}
}
]
}
}
}
}

View file

@ -0,0 +1,461 @@
{
"batchcomplete": "",
"limits": {
"tags": 5000
},
"query": {
"tags": [
{
"name": "abusefilter-condition-limit",
"displayname": "condition limit reached"
},
{
"name": "added youtube video",
"displayname": "contains YouTube embed"
},
{
"name": "advanced mobile edit",
"displayname": "Advanced mobile edit"
},
{
"name": "character-spam",
"displayname": "Character spam"
},
{
"name": "deletiontemplate-addition",
"displayname": "Requested deletion"
},
{
"name": "maps-visual-edit",
"displayname": "Visual map edit"
},
{
"name": "missing signature",
"displayname": "missing signature"
},
{
"name": "mobile edit",
"displayname": "Mobile edit"
},
{
"name": "mobile web edit"
},
{
"name": "mobile-edit",
"displayname": "Mobile edit"
},
{
"name": "move",
"displayname": "move"
},
{
"name": "mw-blank",
"displayname": "Blanking"
},
{
"name": "mw-changed-redirect-target",
"displayname": "Redirect target changed"
},
{
"name": "mw-contentmodelchange",
"displayname": "content model change"
},
{
"name": "mw-new-redirect",
"displayname": "New redirect"
},
{
"name": "mw-removed-redirect",
"displayname": "Removed redirect"
},
{
"name": "mw-replace",
"displayname": "Replaced"
},
{
"name": "mw-rollback",
"displayname": "Rollback"
},
{
"name": "mw-undo",
"displayname": "Undo"
},
{
"name": "possible-number-spam",
"displayname": "possible-number-spam"
},
{
"name": "section blanking",
"displayname": "section blanked"
},
{
"name": "spriteeditor",
"displayname": "<a href=\"/wiki/Help:Sprite_editor\" title=\"Help:Sprite editor\">Sprite edit</a>"
},
{
"name": "test",
"displayname": "test"
},
{
"name": "tor",
"displayname": "Made through Tor"
},
{
"name": "visualeditor",
"displayname": "Visual edit"
},
{
"name": "visualeditor-needcheck",
"displayname": "Visual edit: Check"
},
{
"name": "visualeditor-switched",
"displayname": "<a href=\"/wiki/Minecraft_Wiki:VisualEditor\" title=\"Minecraft Wiki:VisualEditor\">Partial visual edit</a>"
},
{
"name": 0,
"displayname": "0"
},
{
"name": "visualeditor-wikitext",
"displayname": "2017 source edit"
},
{
"name": 1,
"displayname": "1"
},
{
"name": 2,
"displayname": "2"
},
{
"name": 3,
"displayname": "3"
}
],
"allmessages": [
{
"name": "recentchanges-page-added-to-category",
"normalizedname": "recentchanges-page-added-to-category",
"*": "[[:$1]] added to category"
},
{
"name": "recentchanges-page-removed-from-category",
"normalizedname": "recentchanges-page-removed-from-category",
"*": "[[:$1]] removed from category"
},
{
"name": "recentchanges-page-added-to-category-bundled",
"normalizedname": "recentchanges-page-added-to-category-bundled",
"*": "[[:$1]] added to category, [[Special:WhatLinksHere/$1|this page is included within other pages]]"
},
{
"name": "recentchanges-page-removed-from-category-bundled",
"normalizedname": "recentchanges-page-removed-from-category-bundled",
"*": "[[:$1]] removed from category, [[Special:WhatLinksHere/$1|this page is included within other pages]]"
}
],
"namespaces": {
"0": {
"id": 0,
"case": "first-letter",
"subpages": "",
"content": "",
"*": ""
},
"1": {
"id": 1,
"case": "first-letter",
"subpages": "",
"canonical": "Talk",
"*": "Talk"
},
"2": {
"id": 2,
"case": "first-letter",
"subpages": "",
"canonical": "User",
"*": "User"
},
"3": {
"id": 3,
"case": "first-letter",
"subpages": "",
"canonical": "User talk",
"*": "User talk"
},
"4": {
"id": 4,
"case": "first-letter",
"subpages": "",
"canonical": "Project",
"*": "Minecraft Wiki"
},
"5": {
"id": 5,
"case": "first-letter",
"subpages": "",
"canonical": "Project talk",
"*": "Minecraft Wiki talk"
},
"6": {
"id": 6,
"case": "first-letter",
"subpages": "",
"canonical": "File",
"*": "File"
},
"7": {
"id": 7,
"case": "first-letter",
"subpages": "",
"canonical": "File talk",
"*": "File talk"
},
"8": {
"id": 8,
"case": "first-letter",
"subpages": "",
"canonical": "MediaWiki",
"*": "MediaWiki"
},
"9": {
"id": 9,
"case": "first-letter",
"subpages": "",
"canonical": "MediaWiki talk",
"*": "MediaWiki talk"
},
"10": {
"id": 10,
"case": "first-letter",
"subpages": "",
"canonical": "Template",
"*": "Template"
},
"11": {
"id": 11,
"case": "first-letter",
"subpages": "",
"canonical": "Template talk",
"*": "Template talk"
},
"12": {
"id": 12,
"case": "first-letter",
"subpages": "",
"canonical": "Help",
"*": "Help"
},
"13": {
"id": 13,
"case": "first-letter",
"subpages": "",
"canonical": "Help talk",
"*": "Help talk"
},
"14": {
"id": 14,
"case": "first-letter",
"subpages": "",
"canonical": "Category",
"*": "Category"
},
"15": {
"id": 15,
"case": "first-letter",
"subpages": "",
"canonical": "Category talk",
"*": "Category talk"
},
"110": {
"id": 110,
"case": "first-letter",
"canonical": "Forum",
"*": "Forum"
},
"111": {
"id": 111,
"case": "first-letter",
"canonical": "Forum talk",
"*": "Forum talk"
},
"202": {
"id": 202,
"case": "first-letter",
"canonical": "UserProfile",
"*": "UserProfile"
},
"274": {
"id": 274,
"case": "first-letter",
"canonical": "Widget",
"*": "Widget"
},
"275": {
"id": 275,
"case": "first-letter",
"subpages": "",
"canonical": "Widget talk",
"*": "Widget talk"
},
"420": {
"id": 420,
"case": "first-letter",
"canonical": "GeoJson",
"content": "",
"defaultcontentmodel": "GeoJson",
"*": "GeoJson"
},
"421": {
"id": 421,
"case": "first-letter",
"subpages": "",
"canonical": "GeoJson talk",
"defaultcontentmodel": "wikitext",
"*": "GeoJson talk"
},
"500": {
"id": 500,
"case": "first-letter",
"subpages": "",
"canonical": "User blog",
"*": "User blog"
},
"501": {
"id": 501,
"case": "first-letter",
"subpages": "",
"canonical": "User blog comment",
"*": "User blog comment"
},
"502": {
"id": 502,
"case": "first-letter",
"subpages": "",
"canonical": "Blog",
"*": "Blog"
},
"503": {
"id": 503,
"case": "first-letter",
"subpages": "",
"canonical": "Blog talk",
"*": "Blog talk"
},
"828": {
"id": 828,
"case": "first-letter",
"subpages": "",
"canonical": "Module",
"*": "Module"
},
"829": {
"id": 829,
"case": "first-letter",
"subpages": "",
"canonical": "Module talk",
"*": "Module talk"
},
"1200": {
"id": 1200,
"case": "first-letter",
"canonical": "Message Wall",
"*": "Message Wall"
},
"1201": {
"id": 1201,
"case": "first-letter",
"subpages": "",
"canonical": "Thread",
"*": "Thread"
},
"1202": {
"id": 1202,
"case": "first-letter",
"canonical": "Message Wall Greeting",
"*": "Message Wall Greeting"
},
"2300": {
"id": 2300,
"case": "first-letter",
"canonical": "Gadget",
"*": "Gadget"
},
"2301": {
"id": 2301,
"case": "first-letter",
"canonical": "Gadget talk",
"*": "Gadget talk"
},
"2302": {
"id": 2302,
"case": "case-sensitive",
"canonical": "Gadget definition",
"defaultcontentmodel": "GadgetDefinition",
"*": "Gadget definition"
},
"2303": {
"id": 2303,
"case": "case-sensitive",
"canonical": "Gadget definition talk",
"*": "Gadget definition talk"
},
"10000": {
"id": 10000,
"case": "first-letter",
"subpages": "",
"canonical": "Minecraft Dungeons",
"content": "",
"*": "Minecraft Dungeons"
},
"10001": {
"id": 10001,
"case": "first-letter",
"canonical": "Minecraft Dungeons talk",
"*": "Minecraft Dungeons talk"
},
"10002": {
"id": 10002,
"case": "first-letter",
"subpages": "",
"canonical": "Minecraft Earth",
"content": "",
"*": "Minecraft Earth"
},
"10003": {
"id": 10003,
"case": "first-letter",
"canonical": "Minecraft Earth talk",
"*": "Minecraft Earth talk"
},
"10004": {
"id": 10004,
"case": "first-letter",
"subpages": "",
"canonical": "Minecraft Story Mode",
"content": "",
"*": "Minecraft Story Mode"
},
"10005": {
"id": 10005,
"case": "first-letter",
"subpages": "",
"canonical": "Minecraft Story Mode talk",
"*": "Minecraft Story Mode talk"
},
"-2": {
"id": -2,
"case": "first-letter",
"canonical": "Media",
"*": "Media"
},
"-1": {
"id": -1,
"case": "first-letter",
"canonical": "Special",
"*": "Special"
}
}
}
}

View file

@ -0,0 +1,209 @@
{
"batchcomplete": "",
"continue": {
"rccontinue": "20211226120903|2793420",
"continue": "-||"
},
"query": {
"recentchanges": [
{
"type": "edit",
"ns": 0,
"title": "Some different page",
"pageid": 9327,
"revid": 2075232,
"old_revid": 232555,
"rcid": 2793437,
"user": "User3",
"oldlen": 32882,
"newlen": 328,
"timestamp": "2021-12-26T12:26:32Z",
"parsedcomment": "Some changes lol",
"tags": []
},
{
"type": "edit",
"ns": 0,
"title": "Java Edition 1.19",
"pageid": 177589,
"revid": 2075231,
"old_revid": 2075230,
"rcid": 2793436,
"user": "User2",
"minor": "",
"oldlen": 811,
"newlen": 771,
"timestamp": "2021-12-26T12:24:23Z",
"parsedcomment": "Revert edits by <a href=\"/wiki/Special:Contributions/192.168.1.1\" title=\"Special:Contributions/192.168.1.1\">192.168.1.1</a> (<span class=\"new\" title=\"User talk:192.168.1.1 (page does not exist)\">talk</span>)",
"tags": [
"mw-rollback"
]
},
{
"type": "log",
"ns": 2,
"title": "User:FineUser2",
"pageid": 72216,
"revid": 0,
"old_revid": 0,
"rcid": 2793427,
"user": "Administrator2",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T12:40:00Z",
"parsedcomment": "",
"logid": 1141234,
"logtype": "rights",
"logaction": "rights",
"logparams": {
"oldgroups": [
"autopatrol",
"rollback"
],
"newgroups": [
"sysop"
],
"oldmetadata": [
{
"group": "autopatrol",
"expiry": "infinity"
},
{
"group": "rollback",
"expiry": "infinity"
}
],
"newmetadata": [
{
"group": "sysop",
"expiry": "infinity"
}
]
},
"tags": []
},
{
"type": "log",
"ns": 202,
"title": "UserProfile:User2",
"pageid": 0,
"revid": 0,
"old_revid": 0,
"rcid": 2793426,
"user": "User2",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T12:20:11Z",
"parsedcomment": "CoolUser#1812",
"logid": 1141233,
"logtype": "curseprofile",
"logaction": "profile-edited",
"logparams": {
"4:section": "profile-link-discord"
},
"tags": []
},
{
"type": "log",
"ns": 2,
"title": "User:Spammer",
"pageid": 0,
"revid": 0,
"old_revid": 0,
"rcid": 2793425,
"user": "Administrator",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T12:15:04Z",
"parsedcomment": "<a href=\"/wiki/Best_Wiki:Wiki_rules/v3#2\" class=\"mw-redirect\" title=\"Best_Wiki:Wiki rules/v3\">Rule #2</a>: Spam",
"logid": 1141231,
"logtype": "block",
"logaction": "block",
"logparams": {
"duration": "2 weeks",
"flags": [
"nocreate"
],
"sitewide": "",
"expiry": "2022-01-11T10:38:23Z"
},
"tags": []
},
{
"type": "edit",
"ns": 0,
"title": "Java Edition 1.19",
"pageid": 177589,
"revid": 2075225,
"old_revid": 2075224,
"rcid": 2793424,
"user": "Good User",
"anon": "",
"oldlen": 771,
"newlen": 811,
"timestamp": "2021-12-26T12:13:38Z",
"parsedcomment": "Undo revision by 192.168.1.1. Reason: Mad cats",
"tags": [
"visualeditor"
]
},
{
"type": "edit",
"ns": 0,
"title": "Java Edition 1.19",
"pageid": 177589,
"revid": 2075224,
"old_revid": 2074874,
"rcid": 2793423,
"user": "192.168.1.1",
"anon": "",
"oldlen": 811,
"newlen": 771,
"timestamp": "2021-12-26T12:09:18Z",
"parsedcomment": "Hahahahhaha, you will never understand my genius!",
"tags": [
"mobile edit",
"mobile web edit",
"visualeditor"
]
},
{
"type": "categorize",
"ns": 14,
"title": "Category:Mojang images",
"pageid": 181124,
"revid": 2075223,
"old_revid": 0,
"rcid": 2793422,
"user": "User1",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T12:09:04Z",
"parsedcomment": "<a href=\"/wiki/File:Oak_Sign_(9).png\" title=\"File:Oak Sign (9).png\">File:Oak Sign (9).png</a> added to category",
"tags": []
},
{
"type": "log",
"ns": 6,
"title": "File:Oak Sign (9).png",
"pageid": 181124,
"revid": 2075223,
"old_revid": 0,
"rcid": 2793421,
"user": "User1",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T12:09:04Z",
"parsedcomment": "",
"logid": 1141230,
"logtype": "upload",
"logaction": "upload",
"logparams": {
"img_sha1": "l9vzs83denqnn2ph8wya6ieue6b7tvo",
"img_timestamp": "2021-12-26T12:09:04Z"
},
"tags": []
}
]
}
}

View file

@ -0,0 +1,78 @@
{
"batchcomplete": "",
"continue": {
"rccontinue": "20211226120903|2793420",
"continue": "-||"
},
"query": {
"recentchanges": [
{
"type": "edit",
"ns": 0,
"title": "Unique page",
"pageid": 9327,
"revid": 2075234,
"old_revid": 232556,
"rcid": 2793440,
"user": "User3",
"oldlen": 328,
"newlen": 32882,
"timestamp": "2021-12-26T13:37:10Z",
"parsedcomment": "Added content",
"tags": []
},
{
"type": "log",
"ns": 0,
"title": "Some different page",
"pageid": 9327,
"revid": 0,
"old_revid": 0,
"rcid": 2793439,
"user": "Frisk",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T13:35:50Z",
"parsedcomment": "aaaaaaaaaaaaaaa",
"logid": 1141236,
"logtype": "delete",
"logaction": "delete",
"logparams": {},
"tags": []
},
{
"type": "log",
"ns": 0,
"title": "Java Edition 1.19",
"pageid": 177589,
"revid": 0,
"old_revid": 0,
"rcid": 2793438,
"user": "Frisk",
"oldlen": 0,
"newlen": 0,
"timestamp": "2021-12-26T13:02:59Z",
"parsedcomment": "Heresy!",
"logid": 1141235,
"logtype": "delete",
"logaction": "revision",
"logparams": {
"type": "revision",
"ids": [
"2075224"
],
"old": {
"bitmask": 0
},
"new": {
"bitmask": 7,
"content": "",
"comment": "",
"user": ""
}
},
"tags": []
}
]
}
}

View file

@ -0,0 +1,98 @@
{
"batchcomplete": "",
"query": {
"general": {
"mainpage": "Random Wiki",
"base": "https://localhost:8080/wiki/Mainpage",
"sitename": "Minecraft Wiki",
"logo": "https://localhost:8080/localhost/images/b/bc/Wiki.png",
"generator": "MediaWiki 1.35.3",
"phpversion": "7.3.32",
"phpsapi": "fpm-fcgi",
"dbtype": "mysql",
"dbversion": "5.7.25-28-log",
"externalimages": [],
"langconversion": "",
"titleconversion": "",
"linkprefixcharset": "",
"linkprefix": "",
"linktrail": "/^([a-z]+)(.*)$/sD",
"legaltitlechars": " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+",
"invalidusernamechars": "@:",
"fixarabicunicode": "",
"fixmalayalamunicode": "",
"case": "first-letter",
"lang": "en",
"fallback": [],
"fallback8bitEncoding": "windows-1252",
"writeapi": "",
"maxarticlesize": 2097152,
"timezone": "UTC",
"timeoffset": 0,
"articlepath": "/wiki/$1",
"scriptpath": "",
"script": "/index.php",
"variantarticlepath": false,
"server": "https://localhost:8080",
"servername": "localhost:8080",
"wikiid": "localhost",
"time": "2021-12-26T21:57:13Z",
"misermode": "",
"uploadsenabled": "",
"maxuploadsize": 10485760,
"minuploadchunksize": 1024,
"galleryoptions": {
"imagesPerRow": 0,
"imageWidth": 120,
"imageHeight": 120,
"captionLength": "",
"showBytes": "",
"showDimensions": "",
"mode": "traditional"
},
"thumblimits": [
120,
150,
180,
200,
250,
300
],
"imagelimits": [
{
"width": 320,
"height": 240
},
{
"width": 640,
"height": 480
},
{
"width": 800,
"height": 600
},
{
"width": 1024,
"height": 768
},
{
"width": 1280,
"height": 1024
}
],
"favicon": "https://localhost:8080/favicon.ico",
"centralidlookupprovider": "local",
"allcentralidlookupproviders": [
"local"
],
"interwikimagic": "",
"magiclinks": {
"ISBN": ""
},
"categorycollation": "uppercase",
"citeresponsivereferences": "",
"gamepedia": "true",
"mobileserver": "https://localhost:8080"
}
}
}

View file

@ -0,0 +1,22 @@
{
"batchcomplete": "",
"limits": {
"usercontribs": 5000
},
"query": {
"usercontribs": [
{
"userid": 0,
"user": "192.168.1.1"
},
{
"userid": 0,
"user": "192.168.1.1"
},
{
"userid": 0,
"user": "192.168.1.1"
}
]
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
[{"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\uddbc\ufe0f [User1](<http://localhost:8080/wiki/User:User1>) uploaded [File\\:Oak Sign (9).png](<http://localhost:8080/wiki/File:Oak_Sign_%289%29.png>)"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udcdd [Unregistered user](<http://localhost:8080/wiki/User:192.168.1.1>) edited [Java Edition 1.19](<http://localhost:8080/index.php?title=Java_Edition_1.19&curid=177589&diff=2075224&oldid=2074874>) *(Hahahahhaha, you will never understand my genius!)* (-40)"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udcdd [Unregistered user](<http://localhost:8080/wiki/User:Good_User>) edited [Java Edition 1.19](<http://localhost:8080/index.php?title=Java_Edition_1.19&curid=177589&diff=2075225&oldid=2075224>) *(Undo revision by 192.168.1.1. Reason\\: Mad cats)* (+40)"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udeab [Administrator](<http://localhost:8080/wiki/User:Administrator>) blocked [Spammer](<http://localhost:8080/wiki/User:Spammer>) for 15 days, for 22 hours, for 23 minutes *([Rule #2](<http://localhost:8080/wiki/Best_Wiki:Wiki_rules/v3#2>)\\: Spam)*"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udccc [User2](<http://localhost:8080/wiki/User:User2>) edited the Discord handle on [their own](<http://localhost:8080/wiki/UserProfile:User2>) profile. *(CoolUser#1812)*"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83c\udfc5 [Administrator2](<http://localhost:8080/wiki/User:Administrator2>) changed group membership for [FineUser2](<http://localhost:8080/wiki/User:FineUser2>): Added to sysop and removed from autopatrol, rollback."}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udcdd [User2](<http://localhost:8080/wiki/User:User2>) edited [Java Edition 1.19](<http://localhost:8080/index.php?title=Java_Edition_1.19&curid=177589&diff=2075231&oldid=2075230>) *(Revert edits by [192.168.1.1](<http://localhost:8080/wiki/Special:Contributions/192.168.1.1>) (talk))* (-40)"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udcdd [User3](<http://localhost:8080/wiki/User:User3>) edited [Some different page](<http://localhost:8080/index.php?title=Some_different_page&curid=9327&diff=2075232&oldid=232555>) *(Some changes lol)* **(-32554)**"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udc41\ufe0f [Frisk](<http://localhost:8080/wiki/User:Frisk>) changed visibility of revision on page [Java Edition 1.19](<http://localhost:8080/wiki/Java_Edition_1.19>) *(Heresy!)*"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\uddd1\ufe0f [Frisk](<http://localhost:8080/wiki/User:Frisk>) deleted [Some different page](<http://localhost:8080/wiki/Some_different_page>) *(aaaaaaaaaaaaaaa)*"}, {"allowed_mentions": {"parse": []}, "avatar_url": "", "content": "\ud83d\udcdd [User3](<http://localhost:8080/wiki/User:User3>) edited [Unique page](<http://localhost:8080/index.php?title=Unique_page&curid=9327&diff=2075234&oldid=232556>) *(Added content)* **(+32554)**"}]

134
test/mockserver/server.py Normal file
View file

@ -0,0 +1,134 @@
# 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 <http://www.gnu.org/licenses/>.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import urllib.parse
import requests
response_jsons: dict[str, dict] = {}
class EndOfContent(Exception):
pass
def load_response(name: str):
with open("data/response_{}.json".format(name), "r") as response_file:
response_json: dict = json.loads(response_file.read())
response_jsons[name] = response_json
def get_response(name: str):
return response_jsons.get(name)
[load_response(x) for x in ["recentchanges", "recentchanges2", "init", "error", "siteinfo", "image", "userinfo"]]
messages_collector = []
askedfor = False
# Return server response based on some output from Minecraft Wiki
class MockServerRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
global askedfor
# We assume testing will be for API endpoint only since RcGcDw doesn't do requests to other URLs so no need to check main path
# For simplicity, return a dictionary of query arguments, we assume duplicate keys will not appear
query = {k: y for (k, y) in urllib.parse.parse_qsl(self.path.split("?")[1])}
if query.get("action") == "query":
# Regular pooled query for recentchanges
if query.get("list") == "recentchanges":
self.send_essentials_ok()
if askedfor is False:
# Limit amount of events accordingly to required amount just in case
response_jsons["recentchanges"]["query"]["recentchanges"] = get_response("recentchanges")["query"]["recentchanges"][0:int(query.get("rclimit", 20))]
response_content = json.dumps(get_response("recentchanges"))
askedfor = True
else:
response_jsons["recentchanges2"]["query"]["recentchanges"] = get_response("recentchanges2")["query"]["recentchanges"][0:int(query.get("rclimit", 20))]
response_content = json.dumps(get_response("recentchanges2"))
self.wfile.write(response_content.encode('utf-8'))
# Init info
elif query.get("list") == "tags" and query.get("meta") == "allmessages|siteinfo":
self.send_essentials_ok()
response_content = json.dumps(get_response("init"))
self.wfile.write(response_content.encode('utf-8'))
elif query.get("meta") == "siteinfo":
self.send_essentials_ok()
response_content = json.dumps(get_response("siteinfo"))
self.wfile.write(response_content.encode('utf-8'))
elif query.get("prop") == "imageinfo|revisions":
self.send_essentials_ok()
response_content = json.dumps(get_response("image"))
self.wfile.write(response_content.encode('utf-8'))
elif query.get("list") == "usercontribs":
self.send_essentials_ok()
response_content = json.dumps(get_response("userinfo"))
self.wfile.write(response_content.encode('utf-8'))
else:
self.send_response(400)
self.send_header('Content-Type', 'application/json; charset=utf-8')
self.end_headers()
response_content = json.dumps(get_response("error"))
self.wfile.write(response_content.encode('utf-8'))
elif query.get("action") == "compare":
self.send_essentials_ok()
name = "diff{}{}".format(query.get("fromrev"), query.get("torev"))
load_response(name)
response_content = json.dumps(get_response(name))
self.wfile.write(response_content.encode('utf-8'))
def do_POST(self):
self.read_ok_collect(method="POST")
def do_PATCH(self):
self.read_ok_collect(method="PATCH")
def do_DELETE(self):
self.read_ok_collect(method="DELETE")
def read_ok_collect(self, method: str):
content_length = int(self.headers['Content-Length'])
patch_data = self.rfile.read(content_length)
if patch_data:
messages_collector.append(json.loads(patch_data.decode('utf-8')))
else:
messages_collector.append(method + self.path)
self.send_essentials_ok()
self.wfile.write(json.dumps({"id": len(messages_collector)}).encode('utf-8'))
def send_essentials_ok(self):
self.send_response(requests.codes.ok)
self.send_header('Content-Type', 'application/json; charset=utf-8')
self.end_headers()
def start_mock_server(port, config):
mock_server = HTTPServer(('localhost', port), MockServerRequestHandler)
try:
print("Server started successfully at http://localhost:{}".format(port))
while 1:
if (len(messages_collector) < 13 and config.config == 1) or (len(messages_collector) < 11 and config.config == 2):
mock_server.handle_request()
else:
raise EndOfContent
except KeyboardInterrupt:
print("Shutting down...")
except EndOfContent:
with open("results/results{}.json".format(config.config), "r") as proper_results:
if proper_results.read() == json.dumps(messages_collector):
print("Results are correct!")
else:
print("Results are incorrect, saving failed results to resultsfailed{}.json".format(config.config))
with open("results/resultsfailed{}.json".format(config.config), "w") as file_to_write:
file_to_write.write(json.dumps(messages_collector))

56
test/mockserver/start.py Normal file
View file

@ -0,0 +1,56 @@
# 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 <http://www.gnu.org/licenses/>.
import argparse
import json
import server
import pathlib
import shutil
import sys
import time
parser = argparse.ArgumentParser(description="Test RcGcDw with mocked data")
parser.add_argument("--config", type=int, default=1, help="Number of config to use while testing the mocked server (default=1). Number corresponds to files in configs/settings#.json")
parser.add_argument("--ignore-config", action='store_false', help="Ignore lack of existing config.json")
parser.add_argument("--no-client", action='store_true', help="Skip starting the client")
command_args = parser.parse_args()
# Backup old settings.json and copy from configs/settingsX.json to proper relative location
if not command_args.no_client:
new_settings = pathlib.Path(__file__).parent.absolute().joinpath("configs/settings{}.json".format(command_args.config))
old_config = pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve().joinpath("settings.json") # Should be root of RcGcDw
if not old_config.exists() and command_args.ignore_config:
print("Cannot find currently used settings.json! Exiting to prevent potential damage.")
sys.exit(2)
backup_filename = pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve().joinpath("settings.json.{}.bak".format(int(time.time())))
if backup_filename.exists():
print("Backup file under same name exists! Exiting.")
sys.exit(3)
shutil.move(old_config, backup_filename)
shutil.copy(new_settings, old_config)
# revert data file to some low number
with open(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve().joinpath("data.json"), "r") as data_file:
data_file_data = json.loads(data_file.read())
data_file_data["rcid"] = 5
with open(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve().joinpath("data.json"), "w") as data_file:
data_file.write(json.dumps(data_file_data, indent=4))
# Start mock server
server.start_mock_server(8080, command_args)
# Revert file changes
if not command_args.no_client:
shutil.copy(backup_filename, old_config)

File diff suppressed because one or more lines are too long

58
test/test_abusefilter.py Normal file
View file

@ -0,0 +1,58 @@
# 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 <http://www.gnu.org/licenses/>.
import importlib
import json
from src.configloader import settings
from src.api.context import Context
from src.api.util import default_message
from src.api.hooks import formatter_hooks
from src.misc import WIKI_SCRIPT_PATH
from test.test_utilities import inject_settings
from unittest.mock import PropertyMock
import unittest
def no_formatter(ctx: Context, change: dict) -> None:
raise NameError
inject_settings("appearance.mode", "embed")
importlib.import_module(settings.get('extensions_dir', 'extensions'), 'extensions')
formatter_hooks["no_formatter"] = no_formatter
with open("test/data/rc_objects.json", "r") as ob:
jsons = json.loads(ob.read())
with open("test/data/rc_results.json", "r") as ob:
results = json.loads(ob.read())
def get_objects(name: str):
return jsons.get(name), json.dumps(results.get(name))
class TestMWFormatter(unittest.TestCase):
def test_abusefilter_embed(self):
test = default_message("abuselog", formatter_hooks)
ctx = PropertyMock()
ctx.message_type = "embed"
ctx.event_type = "abuselog"
ctx.event = "abuselog"
ctx.parsedcomment = ""
ctx.client.WIKI_SCRIPT_PATH = WIKI_SCRIPT_PATH
ctx.webhook_url = "https://example.com"
# ctx.client.return_value = Mock(spec=Client)
edit_c, results = get_objects("abuselog")
result = repr(test(ctx, edit_c))
self.assertEqual(results, result)

65
test/test_cargo.py Normal file
View file

@ -0,0 +1,65 @@
# 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 <http://www.gnu.org/licenses/>.
import importlib
import json
from src.configloader import settings
from src.api.context import Context
from src.api.util import default_message
from src.api.hooks import formatter_hooks
from src.misc import WIKI_SCRIPT_PATH, LinkParser
from test.test_utilities import inject_settings
from unittest.mock import PropertyMock
import unittest
def no_formatter(ctx: Context, change: dict) -> None:
raise NameError
inject_settings("appearance.mode", "embed")
importlib.import_module(settings.get('extensions_dir', 'extensions'), 'extensions')
formatter_hooks["no_formatter"] = no_formatter
with open("test/data/rc_objects.json", "r") as ob:
jsons = json.loads(ob.read())
with open("test/data/rc_results.json", "r") as ob:
results = json.loads(ob.read())
def get_objects(name: str):
return jsons.get(name), json.dumps(results.get(name))
def parse_links(summary: str):
link_parser = LinkParser()
link_parser.feed(summary)
return link_parser.new_string
class TestMWFormatter(unittest.TestCase):
def test_cargo_embed(self):
test = default_message("cargo/createtable", formatter_hooks)
ctx = PropertyMock()
ctx.message_type = "embed"
ctx.event_type = "cargo/createtable"
ctx.event = "cargo/createtable"
ctx.client.parse_links = parse_links
ctx.parsedcomment = ""
ctx.client.WIKI_SCRIPT_PATH = WIKI_SCRIPT_PATH
ctx.webhook_url = "https://example.com"
# ctx.client.return_value = Mock(spec=Client)
edit_c, results = get_objects("cargo/createtable")
result = repr(test(ctx, edit_c))
self.assertEqual(results, result)

58
test/test_datadump.py Normal file
View file

@ -0,0 +1,58 @@
# 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 <http://www.gnu.org/licenses/>.
import importlib
import json
from src.configloader import settings
from src.api.context import Context
from src.api.util import default_message
from src.api.hooks import formatter_hooks
from src.misc import WIKI_SCRIPT_PATH
from test.test_utilities import inject_settings
from unittest.mock import PropertyMock
import unittest
def no_formatter(ctx: Context, change: dict) -> None:
raise NameError
inject_settings("appearance.mode", "embed")
importlib.import_module(settings.get('extensions_dir', 'extensions'), 'extensions')
formatter_hooks["no_formatter"] = no_formatter
with open("test/data/rc_objects.json", "r") as ob:
jsons = json.loads(ob.read())
with open("test/data/rc_results.json", "r") as ob:
results = json.loads(ob.read())
def get_objects(name: str):
return jsons.get(name), json.dumps(results.get(name))
class TestMWFormatter(unittest.TestCase):
def test_datadump_embed(self):
test = default_message("datadump/generate", formatter_hooks)
ctx = PropertyMock()
ctx.message_type = "embed"
ctx.event_type = "datadump/generate"
ctx.event = "datadump/generate"
ctx.parsedcomment = ""
ctx.client.WIKI_SCRIPT_PATH = WIKI_SCRIPT_PATH
ctx.webhook_url = "https://example.com"
# ctx.client.return_value = Mock(spec=Client)
edit_c, results = get_objects("datadump/generate")
result = repr(test(ctx, edit_c))
self.assertEqual(results, result)

View file

@ -0,0 +1,31 @@
# 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 <http://www.gnu.org/licenses/>.
import unittest
from test.test_utilities import inject_settings
from src.wiki import Wiki
class login_Testing(unittest.TestCase):
wiki = Wiki(None, None)
def test_connection_checker(self):
self.assertTrue(self.wiki.check_connection(looped=True))
def test_connection_tracker1(self): # expands this test
inject_settings("show_updown_messages", True)
self.wiki.downtimecredibility = 0
self.wiki.downtime_controller(True)
self.assertTrue(self.wiki.downtimecredibility > 0)

82
test/test_hooks.py Normal file
View file

@ -0,0 +1,82 @@
# 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 <http://www.gnu.org/licenses/>.
import unittest
from src.api.context import Context
from src.discord.message import DiscordMessage, DiscordMessageMetadata
from src.api import formatter
from src.api.hook import pre_hook, post_hook
from src.api.hooks import formatter_hooks, pre_hooks, post_hooks
class ApiTesting(unittest.TestCase):
def setUp(self) -> None:
self.temp = formatter_hooks.copy()
def tearDown(self) -> None:
formatter_hooks.update(self.temp)
def test_embed_formatter_registration(self):
formatter_hooks.clear()
@formatter.embed(event="test", mode="embed")
def test_formatter_registration(ctx: Context, change: dict) -> DiscordMessage:
pass
self.assertEqual(formatter_hooks["test"], test_formatter_registration)
def test_compact_formatter_registration(self):
formatter_hooks.clear()
@formatter.embed(event="test", mode="compact")
def test_formatter_registration(ctx: Context, change: dict) -> DiscordMessage:
pass
self.assertEqual(formatter_hooks["test"], test_formatter_registration)
def test_overwrite_formatter_registration_warning(self):
formatter_hooks.clear()
@formatter.embed(event="test", mode="compact")
def test_formatter_registration(ctx: Context, change: dict) -> DiscordMessage:
pass
with self.assertLogs("src.api.formatter", level="WARNING"):
@formatter.embed(event="test", mode="compact")
def test_other_formatter_registration(ctx: Context, change: dict) -> DiscordMessage:
pass
def test_formatter_aliasing(self):
formatter_hooks.clear()
@formatter.embed(event="test", mode="compact", aliases=["test2", "test3"])
def test_formatter_registration(ctx: Context, change: dict) -> DiscordMessage:
pass
self.assertEqual(formatter_hooks["test2"], test_formatter_registration)
def test_pre_hook_registration(self):
pre_hooks.clear()
@pre_hook
def test_prehook(some_data: Context, change: dict):
pass
self.assertEqual(pre_hooks[0], test_prehook)
def test_post_hook_registration(self):
post_hooks.clear()
@post_hook
def test_posthook(message: DiscordMessage, metadata: DiscordMessageMetadata, context: Context, change: dict):
pass
self.assertEqual(post_hooks[0], test_posthook)

30
test/test_i18n.py Normal file
View file

@ -0,0 +1,30 @@
# 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 <http://www.gnu.org/licenses/>.
from test.test_utilities import inject_settings
import src.i18n
import unittest
class i18nTesting(unittest.TestCase):
def test_language_output_polish(self):
inject_settings("lang", "pl")
src.i18n.load_languages() # reload languages with new language
self.assertEqual(src.i18n.rcgcdw.gettext("Daily overview"), "Podsumowanie dnia")
def test_language_output_english(self):
inject_settings("lang", "en")
src.i18n.load_languages() # reload languages with new language
self.assertEqual(src.i18n.rcgcdw.gettext("Daily overview"), "Daily overview")

58
test/test_interwiki.py Normal file
View file

@ -0,0 +1,58 @@
# 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 <http://www.gnu.org/licenses/>.
import importlib
import json
from src.configloader import settings
from src.api.context import Context
from src.api.util import default_message
from src.api.hooks import formatter_hooks
from src.misc import WIKI_SCRIPT_PATH
from test.test_utilities import inject_settings
from unittest.mock import PropertyMock
import unittest
def no_formatter(ctx: Context, change: dict) -> None:
raise NameError
inject_settings("appearance.mode", "embed")
importlib.import_module(settings.get('extensions_dir', 'extensions'), 'extensions')
formatter_hooks["no_formatter"] = no_formatter
with open("test/data/rc_objects.json", "r") as ob:
jsons = json.loads(ob.read())
with open("test/data/rc_results.json", "r") as ob:
results = json.loads(ob.read())
def get_objects(name: str):
return jsons.get(name), json.dumps(results.get(name))
class TestMWFormatter(unittest.TestCase):
def test_interwiki_embed(self):
test = default_message("interwiki/iw_add", formatter_hooks)
ctx = PropertyMock()
ctx.message_type = "embed"
ctx.event_type = "interwiki/iw_add"
ctx.event = "interwiki/iw_add"
ctx.parsedcomment = ""
ctx.client.WIKI_SCRIPT_PATH = WIKI_SCRIPT_PATH
ctx.webhook_url = "https://example.com"
# ctx.client.return_value = Mock(spec=Client)
edit_c, results = get_objects("interwiki/iw_add")
result = repr(test(ctx, edit_c))
self.assertEqual(results, result)

58
test/test_mediawiki.py Normal file
View file

@ -0,0 +1,58 @@
# 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 <http://www.gnu.org/licenses/>.
import importlib
import json
from src.configloader import settings
from src.api.context import Context
from src.api.util import default_message
from src.api.hooks import formatter_hooks
from src.misc import WIKI_SCRIPT_PATH
from test.test_utilities import inject_settings
from unittest.mock import PropertyMock
import unittest
def no_formatter(ctx: Context, change: dict) -> None:
raise NameError
inject_settings("appearance.mode", "embed")
importlib.import_module(settings.get('extensions_dir', 'extensions'), 'extensions')
formatter_hooks["no_formatter"] = no_formatter
with open("test/data/rc_objects.json", "r") as ob:
jsons = json.loads(ob.read())
with open("test/data/rc_results.json", "r") as ob:
results = json.loads(ob.read())
def get_objects(name: str):
return jsons.get(name), json.dumps(results.get(name))
class TestMWFormatter(unittest.TestCase):
def test_edit_embed(self):
test = default_message("edit", formatter_hooks)
ctx = PropertyMock()
ctx.message_type = "embed"
ctx.event_type = "edit"
ctx.event = "edit"
ctx.parsedcomment = "Work on new as"
ctx.client.WIKI_SCRIPT_PATH = WIKI_SCRIPT_PATH
ctx.webhook_url = "https://example.com"
# ctx.client.return_value = Mock(spec=Client)
edit_c, results = get_objects("edit")
result = repr(test(ctx, edit_c))
self.assertEqual(results, result)

76
test/test_misc.py Normal file
View file

@ -0,0 +1,76 @@
# 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 <http://www.gnu.org/licenses/>.
# 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 <http://www.gnu.org/licenses/>.
import json
import sys
from unittest import TestCase
from src.exceptions import MediaWikiError
from src.misc import datafile, data_template, weighted_average, prepare_paths, parse_mw_request_info
from os.path import exists
class TestDataFile(TestCase):
def test_generate_datafile(self):
datafile.generate_datafile()
self.assertTrue(exists(datafile.data_filename))
with open(datafile.data_filename, "r") as df:
contents = df.read()
print(json.loads(contents))
self.assertEqual(json.loads(contents), data_template)
def test_load_datafile(self):
self.assertEqual(datafile.load_datafile(), data_template)
# def test_save_datafile(self):
# datafile["discussion_id"] = 321388838283
# datafile.save_datafile()
# with open(datafile.data_filename, "r") as df:
# contents = json.loads(df.read())
# self.assertEqual(contents["discussion_id"], 321388838283)
class Test(TestCase):
def test_weighted_average(self):
self.assertEqual(weighted_average(3, 5, 30), 7.5)
def test_prepare_paths(self):
self.assertEqual(prepare_paths("https://minecraft.fandom.com/blabhlldlasldllad", dry=True), "https://minecraft.fandom.com")
self.assertEqual(prepare_paths("https://minecraft.fandom.com/wiki/Minecraft_Wiki", dry=True), "https://minecraft.fandom.com")
self.assertEqual(prepare_paths("https://minecraft.fandom.com/", dry=True), "https://minecraft.fandom.com")
def test_parse_mw_request_info(self):
warning_data = """{"batchcomplete":"","warnings":[{"code":"unrecognizedvalues","key":"apiwarn-unrecognizedvalues","params":["list",{"list":["recentchange"],"type":"comma"},1],"module":"query"},{"code":"unrecognizedparams","key":"apierror-unrecognizedparams","params":[{"list":{"3":"rcshow","4":"rcprop","5":"rclimit","6":"rctype"},"type":"comma"},4],"module":"main"}]}"""
warning_data = json.loads(warning_data)
error_data = """{"errors":[{"code":"missingparam","key":"apierror-missingparam-at-least-one-of","params":[{"list":["<var>totitle</var>","<var>toid</var>","<var>torev</var>","<var>totext</var>","<var>torelative</var>","<var>toslots</var>"],"type":"text"},6],"module":"compare"}],"*":"See https://minecraft.fandom.com/pl/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."}"""
error_data = json.loads(error_data)
self.assertRaises(MediaWikiError, parse_mw_request_info, error_data, "dummy")
with self.assertLogs("rcgcdw.misc", level="WARNING"):
parse_mw_request_info(warning_data, "dummy")
# with self.assertNoLogs("rcgcdw.misc", level="WARNING"): # python 3.10
# parse_mw_request_info(legit_data, "dummy")

66
test/test_queue.py Normal file
View file

@ -0,0 +1,66 @@
# 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 <http://www.gnu.org/licenses/>.
import unittest
from typing import Tuple
from src.discord.queue import MessageQueue
from src.discord.message import DiscordMessage, DiscordMessageMetadata
def create_dummy(id: int = 0, **kwargs) -> Tuple[DiscordMessage, DiscordMessageMetadata]:
dm = DiscordMessage(event_type="log/{}".format(id), message_type="embed", webhook_url="https://example.com/")
dmm = DiscordMessageMetadata("POST", log_id=kwargs.get("log_id", int(id)*10), page_id=kwargs.get("page_id", int(id)),
rev_id=kwargs.get("rev_id", int(id)*10), webhook_url=kwargs.get("webhook_url", "https://example.com/"))
return dm, dmm
class TestQueue(unittest.TestCase):
def test_add_message(self):
queue = MessageQueue()
for _ in range(100):
queue.add_message(create_dummy())
self.assertEqual(len(queue), 100)
def test_cut_messages(self):
queue = MessageQueue()
for num in range(100):
queue.add_message(create_dummy(id=num))
queue.cut_messages(10)
self.assertEqual(list(queue)[0][1].page_id, 10)
def test_compare_message_to_dict(self):
queue = MessageQueue()
passing = [create_dummy(id=103, page_id=3928, rev_id=228848), create_dummy(id=108, page_id=3928, rev_id=228853)]
failing = [create_dummy(id=105, page_id=39, rev_id=2288), create_dummy(id=110, page_id=392, rev_id=228)]
for msg in passing:
with self.subTest():
self.assertTrue(queue.compare_message_to_dict(msg[1], {"page_id": 3928}))
for msg in failing:
with self.subTest():
self.assertFalse(queue.compare_message_to_dict(msg[1], {"page_id": 3928}))
def test_delete_all_with_matching_metadata(self):
queue = MessageQueue()
queue.add_message(create_dummy(id=103, page_id=500, rev_id=228844))
for num in range(100):
queue.add_message(create_dummy(id=num))
queue.add_message(create_dummy(id=105, page_id=3923, rev_id=228848))
queue_correct = MessageQueue()
for num in range(100):
queue_correct.add_message(create_dummy(id=num))
queue_correct.add_message(create_dummy(id=105, page_id=3923, rev_id=228848))
queue.delete_all_with_matching_metadata(page_id=500)
self.assertEqual(len(queue), len(queue_correct)) # Could be better but I'm running out of time

25
test/test_utilities.py Normal file
View file

@ -0,0 +1,25 @@
# 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 <http://www.gnu.org/licenses/>.
from typing import Any
import src.configloader
def inject_settings(setting: str, value: Any):
path = setting.split(".")
dic = src.configloader.settings
for key in path[:-1]:
dic = dic[key]
dic[path[-1]] = value

53
test/test_wiki_login.py Normal file
View file

@ -0,0 +1,53 @@
# 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 <http://www.gnu.org/licenses/>.
import unittest
from src.configloader import settings
from test.test_utilities import inject_settings
from src.wiki import Wiki
def cleanup_func(func):
def wrap(*args, **kwargs):
login = settings["wiki_bot_login"]
password = settings["wiki_bot_password"]
func(*args, **kwargs)
inject_settings("wiki_bot_login", login)
inject_settings("wiki_bot_password", password)
return func
return wrap
class login_Testing(unittest.TestCase):
wiki = Wiki(None, None)
def test_success(self):
self.wiki.logged_in = False
self.wiki.log_in()
self.assertTrue(self.wiki.logged_in)
@cleanup_func
def test_failure1(self):
self.wiki.logged_in = False
inject_settings("wiki_bot_login", "asdkaodhasofaufbasf")
with self.assertLogs("rcgcdw.rc", level="ERROR"):
self.wiki.log_in()
@cleanup_func
def test_failure2(self):
self.wiki.logged_in = False
inject_settings("wiki_bot_password", "lkkkkk")
with self.assertLogs("rcgcdw.rc", level="ERROR"):
self.wiki.log_in()