From 079669a6d3931c69b73eede086bf3ccbf74cff83 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Sat, 15 Oct 2022 20:15:33 -0700 Subject: [PATCH] skeleton of interacting with mediawiki. abandoning pywikibot moving discord stuff back into a class --- poetry.lock | 40 +++++++- pyproject.toml | 1 + wiki_postbot/clients/discord_client.py | 128 ++++++++++++------------- wiki_postbot/creds.py | 12 +++ wiki_postbot/interfaces/mediawiki.py | 86 +++++++++++++++++ 5 files changed, 202 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index ee13059..292a233 100644 --- a/poetry.lock +++ b/poetry.lock @@ -292,6 +292,14 @@ scripts = ["isbnlib", "unidecode", "mwparserfromhell (>=0.5.0)", "memento-client "weblinkchecker.py" = ["memento-client (==0.6.1)"] wikitextparser = ["wikitextparser (>=0.47.5)", "wikitextparser (>=0.47.0)"] +[[package]] +name = "regex" +version = "2022.9.13" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "requests" version = "2.28.0" @@ -388,6 +396,30 @@ brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "wikitextparser" +version = "0.51.1" +description = "A simple parsing tool for MediaWiki's wikitext markup." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +regex = ">=2022.9.11" +wcwidth = "*" + +[package.extras] +dev = ["path.py", "coverage", "twine"] +tests = ["pytest"] + [[package]] name = "yarl" version = "1.8.1" @@ -403,7 +435,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "a12006faa296b74e34d600ab7300f2926d58c153fa76c15a5b73f7c91077b6ab" +content-hash = "061b7fa1deb95223cf42da4bf28189c4e64a837bc00ff1617c1e66569184ac01" [metadata.files] aiohttp = [] @@ -481,6 +513,7 @@ python-dateutil = [ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] pywikibot = [] +regex = [] requests = [ {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, @@ -509,4 +542,9 @@ urllib3 = [ {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, ] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +wikitextparser = [] yarl = [] diff --git a/pyproject.toml b/pyproject.toml index c052856..3657eb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ parse = "^1.19.0" pywikibot = "^7.7.0" pyparsing = "^3.0.9" "discord.py" = "^2.0.1" +wikitextparser = "^0.51.1" [tool.poetry.dev-dependencies] pytest = "^7.1.2" diff --git a/wiki_postbot/clients/discord_client.py b/wiki_postbot/clients/discord_client.py index 9530f16..a2bd000 100644 --- a/wiki_postbot/clients/discord_client.py +++ b/wiki_postbot/clients/discord_client.py @@ -5,75 +5,75 @@ from wiki_postbot.patterns.wikilink import Wikilink from discord.ext import commands from discord import Emoji +# +# intents = Intents.default() +# intents.message_content = True +# +# bot = commands.Bot(command_prefix='/', intents=intents) +# +# DEBUG = False +# +# @bot.event +# async def on_message(message:discord.Message): +# print(message) +# +# if message.content == 'ping': +# await message.channel.send('pong') +# +# if 'good bot' in message.content: +# await message.add_reaction("❤️‍🔥") +# +# wl = Wikilink.parse(message.content) +# if len(wl) > 0: +# if DEBUG: +# await message.channel.send(f"Wikilinks detected: \n" + '\n'.join([str(l) for l in wl])) +# else: +# await message.add_reaction("⏳") +# +# await bot.process_commands(message) +# +# @bot.command() -intents = Intents.default() -intents.message_content = True - -bot = commands.Bot(command_prefix='/', intents=intents) - -DEBUG = False - -@bot.event -async def on_message(message:discord.Message): - print(message) - - if message.content == 'ping': - await message.channel.send('pong') - - if 'good bot' in message.content: - await message.add_reaction("❤️‍🔥") - - wl = Wikilink.parse(message.content) - if len(wl) > 0: - if DEBUG: - await message.channel.send(f"Wikilinks detected: \n" + '\n'.join([str(l) for l in wl])) - else: - await message.add_reaction("⏳") - - await bot.process_commands(message) - -@bot.command() -async def debug(ctx:discord.ext.commands.Context, arg): - print('debug command') - global DEBUG - if arg == "on": - DEBUG = True - await ctx.message.add_reaction("🧪") - elif arg == "off": - DEBUG = False - await ctx.message.add_reaction("🤐") - else: - await ctx.message.reply("usage: /debug off or /debug on") # @bot.command -# -# class MyClient(Client): -# -# def __init__(self, intents=None, **kwargs): -# if intents is None: -# intents = Intents.default() -# intents.message_content = True -# -# super(MyClient, self).__init__(intents=intents, **kwargs) -# -# -# async def on_ready(self): -# print('Logged on as', self.user) -# -# async def on_message(self, message): -# print(message) -# # don't respond to ourselves -# if message.author == self.user: -# return -# -# if message.content == 'ping': -# await message.channel.send('pong') -# -# wl = Wikilink.parse(message.content) -# if len(wl)>0: -# await message.channel.send(f"Wikilinks detected: \n" + '\n'.join([str(l) for l in wl])) +class MyClient(Client): + + def __init__(self, intents=None, debug:bool=False, **kwargs): + if intents is None: + intents = Intents.default() + intents.message_content = True + + self.debug = debug + + super(MyClient, self).__init__(intents=intents, **kwargs) + + + async def on_ready(self): + print('Logged on as', self.user) + + async def on_message(self, message): + print(message) + # don't respond to ourselves + if message.author == self.user: + return + + wl = Wikilink.parse(message.content) + if len(wl)>0: + await message.channel.send(f"Wikilinks detected: \n" + '\n'.join([str(l) for l in wl])) + + async def debug(ctx: discord.ext.commands.Context, arg): + print('debug command') + global DEBUG + if arg == "on": + DEBUG = True + await ctx.message.add_reaction("🧪") + elif arg == "off": + DEBUG = False + await ctx.message.add_reaction("🤐") + else: + await ctx.message.reply("usage: /debug off or /debug on") if __name__ == "__main__": diff --git a/wiki_postbot/creds.py b/wiki_postbot/creds.py index 2779792..c3dad62 100644 --- a/wiki_postbot/creds.py +++ b/wiki_postbot/creds.py @@ -41,3 +41,15 @@ class Discord_Creds: with open(path, 'r') as jfile: creds = json.load(jfile) return Discord_Creds(**creds) + + +@dataclass +class Mediawiki_Creds: + user:str + password:str + + @classmethod + def from_json(cls, path:Path) -> 'Mediawiki_Creds': + with open(path, 'r') as jfile: + creds = json.load(jfile) + return Mediawiki_Creds(**creds) \ No newline at end of file diff --git a/wiki_postbot/interfaces/mediawiki.py b/wiki_postbot/interfaces/mediawiki.py index 858d2e9..836a670 100644 --- a/wiki_postbot/interfaces/mediawiki.py +++ b/wiki_postbot/interfaces/mediawiki.py @@ -1,4 +1,90 @@ """ Class for interfacing with mediawiki """ +from typing import List +from urllib.parse import urljoin +from dataclasses import dataclass +from wiki_postbot.creds import Mediawiki_Creds +import requests +# creds = Mediawiki_Creds.from_json('mediawiki_creds.json') + + + + +class Wiki: + def __init__(self, url:str, api_suffix:str="/api.php"): + self.url = url + self.api_url = urljoin(self.url, api_suffix) + self.sess = None + + + def login(self, creds:Mediawiki_Creds) -> requests.Session: + # get token to log in + sess = requests.Session() + + login_token = sess.get( + self.api_url, + params={ + "action":"query", + "meta":"tokens", + "type":"login", + "format":"json" + }, + verify=False + ).json()['query']['tokens']['logintoken'] + + + login_result = sess.post( + self.api_url, + data = { + "action":"login", + "lgname":creds.user, + "lgpassword":creds.password, + "lgtoken": login_token, + "format": "json" + }, + verify=False + ) + assert login_result.json()['login']['result'] == "Success" + self.sess = sess + return sess + + def get_page_content(self, page:str) -> str: + + content = self.sess.get( + self.api_url, + params={ + 'action':'parse', + 'page': page, + 'prop': 'wikitext', + 'formatversion':'2', + 'format':'json' + } + ).json() + return content['parse']['wikitext'] + + def insert_text(self, page, section, text): + + token = self.sess.get( + self.api_url, + params={ + "action": "query", + "meta": "tokens", + "format": "json" + }, + verify=False + ).json()['query']['tokens']['csrftoken'] + + result = self.sess.post( + self.api_url, + data={ + "action":"edit", + "title":page, + "section":"new", + "sectiontitle":section, + "appendtext":text, + "format":"json", + "token":token + } + )