From d00096bb99d780339f3c4a41dd88bf25a339ddbc Mon Sep 17 00:00:00 2001 From: RhinosF1 Date: Tue, 18 Aug 2020 22:45:57 +0100 Subject: [PATCH] Release 9.0.0 (#291) * travis: make deploy last and test first (#254) * copied from https://github.com/sopel-irc/sopel/commit/a33caf15090d61b90dc831f55cc195e56185dad3 * dev-require: require flake8 See #255 * Introduce utils.mwapihandler and a wikimgnt plugin and make status use mwapihandler (#252) * Let's test this and see what happens Co-authored-by: MacFan4000 * Revert "Introduce utils.mwapihandler and a wikimgnt plugin and make status use mwapihandler (#252)" (#257) This reverts commit cce7fb29c4b0262a9092b57289cc0cba081af9c5. * Introduce utils.mwapihandler, a wikimgnt plugin and also make status use the new mwapihandler (#258) Let's try this, take 2 Co-authored-by: MacFan4000 * status: fix config rename * mwapihandler: return a response if login works * Update mwapihandler.py * mwapihandler: fix spelling of logintoken * Fix wikifarm * Update mwapihandler.py * mwapihandler: drop redirect from 'create' * testing * Update mwapihandler.py * Update status.py * Update status.py * status: hardcode parameters * mwapihandler: actually pass content if it exists * status: stop parsing trigger in updatestatus See #259 This drops the trigger.* and options from being passed to the updatestatus(..) method and replaces it with a requestdata array containing the data for the request. By doing this, we ensure that the passed data to updatestatus is validated and can better handle exceptions. Resolves T61 * joinall: stop checking channel * joinall: fix indentation * status: replace a few removed variables * Fix LGTM errors/warnings (#260) See #260 NOOP * drop __future__ See #261 * travis: Stop blacklisting E402 (#262) * travis: stop blacklisting F401 (#264) * travis: stop blacklisting F401 * wikimgnt: drop unused imports * wikimgnt: drop unused import * status: drop unused import * mwapihandler: drop unused import * models: drop unused imports * test_rss: drop unused imports * module -> plugin (#263) See #263 NOOP * travis: stop blacklisting W503 See #265 * travis: remove all ignores (#266) * travis: remove all ignores * Update wikimgnt.py * Update mwapihandler.py * Update mwapihandler.py * wikimgnt: refactor deletepage() * wikimgnt: more refactoring * wikimgnt: if -> elif * Update wikimgnt.py * wikimgnt: add blockManager and make (un)block user it * wikimgnt: fix syntax help for blockManger * wikimgnt: + trigger for blockManager * wikimgnt: cleanup * Update wikimgnt.py * Fix block/unblock Target/reason is different for wikifarm * Fix * Link user page in edit summary Per suggestion of dmehus * Revert * Fix devoice * Bump setuptools from 49.2.1 to 49.3.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.2.1 to 49.3.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.2.1...v49.3.0) Signed-off-by: dependabot-preview[bot] * Add Json Parser See #269 Introduce a util script for the parsing of json. Bug: T64 * status: remove unneeded variable assignment * mwapihandler: use CONSTANT for CONNECTERRMSG * mapihandler: fix * mwapihandler: rename TOKEN -> token inside makeaction(..) (#270) * Update phab.py * [Security] Stop using http:// for rss tests * mwapihandler: remove unused variable * rss: fix variable assigned twice * ZppixBot -> MirahezeBot (#272) * Update requirements.txt * Update responses.py Co-authored-by: RhinosF1 * channelmgnt: cache json in memory See #273 Resolves T64 * channelmgnt: bug fix * Update channelmgnt.py * Update phab.py * Bump setuptools from 49.3.0 to 49.3.1 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.3.0 to 49.3.1. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.3.0...v49.3.1) Signed-off-by: dependabot-preview[bot] * jsonparser: add cache validation / channelmgnt: add command to validate cache See #276 * channelgmnt: apparently trigger is given by force * channelmgnt: switch to makemodechange(...) See #277 * channelmgnt: revert a tiny bit * channelmgnt: fix * channelmgnt: clean up * status: switch to json logic See #278 * status: sp * Update status.py * status: fix * status: add error * status: fix * status: debug * status: access right section * Bump setuptools from 49.3.1 to 49.3.2 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.3.1 to 49.3.2. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.3.1...v49.3.2) Signed-off-by: dependabot-preview[bot] * phab: add cache handler and warn on old config See #280 PR is no-op in production * Phab use multi url Take 2 See #281 * Update phab.py * Revert last 3 commits (phab update) (#282) * Revert "Update phab.py" This reverts commit 904c00fd9b3615ecdc5d80ff0dfa528d63193884. * Revert "Phab use multi url Take 2" This reverts commit 68efef537c0501ca2ef0723aaec4cb003bb03922. * Revert "phab: add cache handler and warn on old config" This reverts commit 210b7449e62c237cec47e6dd95bf8cf8b77e993c. * Phab: Take 4 (#283) * Revert "Revert last 3 commits (phab update) (#282)" This reverts commit 11a7c4c98fcf723e6c55822d84b4a6f9a17abd62. * phab: take 4 * Update phab.py * Update phab.py * Update phab.py * Update phab.py * phab: use new URL from host for output See #284 * Update phab.py * phab: remove debug code * Bump setuptools from 49.3.2 to 49.4.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.3.2 to 49.4.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.3.2...v49.4.0) Signed-off-by: dependabot-preview[bot] * Bump setuptools from 49.4.0 to 49.5.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.4.0 to 49.5.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.4.0...v49.5.0) Signed-off-by: dependabot-preview[bot] * Bump setuptools from 49.5.0 to 49.6.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 49.5.0 to 49.6.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/master/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v49.5.0...v49.6.0) Signed-off-by: dependabot-preview[bot] * status: stop passing bot.* to updatestatus(..) * status: fix * mwapi: fix too many parameters code smell See #288 * use cache for wikimgnt (#289) * use cache for wikimgnt * fix travis * fixes * fix * fixes * more fixes * more fixes * caps * remove duplicate variable definition * Update wikimgnt.py * fix * add debug * fixes * fix * fix * fixes * fix * fix * urgh * fix * fix * fixes * Update wikimgnt.py * Update wikimgnt.py * Update wikimgnt.py * Update wikimgnt.py * wikimgnt: back out of 9.0.0 Maybe restored if I have time but in it's current state it's not production/stable code. I can see multiple things that don't make sense, unhandled errors, ignored suggestions and things that don't return right. * Revert wikimgnt json (#292) * Revert "Update wikimgnt.py" This reverts commit 360affa0c21b8bbd323e8af809984481efb4982d. * Revert "Update wikimgnt.py" This reverts commit 2568ac2f25d46530cc8f18ff38d4a3bbfa3799a8. * Revert "Update wikimgnt.py" This reverts commit 537d409730259face738f9cddd80f4854d33dd94. * Revert "Update wikimgnt.py" This reverts commit f5e2f260a4678093d14a85b49cf88562c1ba576c. * Revert "fixes" This reverts commit 9b13cc2e091a435650e59c05881eccc054e320c9. * Revert "fix" This reverts commit 7a8468163c17c9c3dfa15b9ed8ce7322cbe3c8fa. * Revert "fix" This reverts commit e3545281bde98de48a96070661a74e8735532202. * Revert "urgh" This reverts commit 70ef23bbbc71991bd7bdf992168df59aa9c38df1. * Revert "fix" This reverts commit caa9ae6b31c42d3af45680acd72219ad78f3957f. * Revert "fix" This reverts commit e2ab4571c6801f4d4f488ede99e4648de832c100. * Revert "fixes" This reverts commit 4cf579354a51c1c8020457dfadb132428de3dc4f. * Revert "fix" This reverts commit 6540f465901dbc9e7fd46e2467af14a200cc951f. * Revert "fix" This reverts commit b4108de5e5d114912af429b8ea497faa267c07d9. * Revert "fixes" This reverts commit f7834af06f322435f8d95314cce61b4a28bc6c9e. * Revert "add debug" This reverts commit d8668379b07ab27386040016c2baad80975c7cb1. * Revert "fix" This reverts commit de3200a4d21aa83d7e30df22f75e349f8b60e668. * Revert "use cache for wikimgnt (#289)" This reverts commit d54cd0f917484290a8172ea1e5f8c7574849a410. * Rework to not pass bot and trigger phab T69 #2 (#293) * Update welcome.py Fixed welcome.py send_welcome to not pass bot and trigger * Spaces to Tab * autopep8 results Change spacing with autopep8 for .... 'tool happiness' Co-authored-by: RhinosF1 Co-authored-by: Operator873 * test_json: add json parser tests See #294 * Rework to not pass bot and trigger phab T69 #2 (#293) * Update welcome.py Fixed welcome.py send_welcome to not pass bot and trigger * Spaces to Tab * autopep8 results Change spacing with autopep8 for .... 'tool happiness' Co-authored-by: RhinosF1 Co-authored-by: Operator873 * test_json: add json parser tests See #294 * bump dev to v9 See #275 Bug: T63 * bump dev to v9 See #275 Bug: T63 * Bump sqlalchemy from 1.3.18 to 1.3.19 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.3.18 to 1.3.19. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) Signed-off-by: dependabot-preview[bot] * Version 9.0.0 changelog * Update CHANGELOG.md Co-authored-by: MacFan4000 Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Operator873 <49728480+Operator873@users.noreply.github.com> Co-authored-by: Operator873 Co-authored-by: Devin/Zppix --- .travis.yml | 32 ++- CHANGELOG.md | 21 ++ MirahezeBots/plugins/adminlist.py | 9 +- MirahezeBots/plugins/channelmgnt.py | 315 ++++++++-------------------- MirahezeBots/plugins/goofy.py | 22 +- MirahezeBots/plugins/joinall.py | 24 +-- MirahezeBots/plugins/miraheze.py | 9 +- MirahezeBots/plugins/phab.py | 112 +++++++--- MirahezeBots/plugins/pingpong.py | 9 +- MirahezeBots/plugins/responses.py | 7 +- MirahezeBots/plugins/rss.py | 12 +- MirahezeBots/plugins/shortlinks.py | 7 +- MirahezeBots/plugins/status.py | 264 ++++++++--------------- MirahezeBots/plugins/welcome.py | 50 ++--- MirahezeBots/utils/__init__.py | 1 + MirahezeBots/utils/jsonparser.py | 15 ++ MirahezeBots/utils/mwapihandler.py | 127 +++++++++++ dev-requirements.txt | 3 +- requirements.txt | 5 +- setup.py | 4 +- tests/models.py | 5 +- tests/test_general.py | 2 +- tests/test_json.json | 17 ++ tests/test_json.py | 20 ++ tests/test_rss.py | 98 ++++----- 25 files changed, 594 insertions(+), 596 deletions(-) create mode 100644 MirahezeBots/utils/__init__.py create mode 100644 MirahezeBots/utils/jsonparser.py create mode 100644 MirahezeBots/utils/mwapihandler.py create mode 100644 tests/test_json.json create mode 100644 tests/test_json.py diff --git a/.travis.yml b/.travis.yml index b907d888..dc37e657 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ dist: xenial os: linux install: - - pip install flake8 - pip install -r dev-requirements.txt - pip install . before_script: @@ -17,11 +16,12 @@ before_script: - pip show MirahezeBot-Plugins script: - # we ignore E402 because of workarounds we use for modules loading, and F401 because some imports aren't directly used but needed for modules and travis doesn't like that - - flake8 MirahezeBots/plugins --ignore E402,F401,W503,E722 --max-line-length 220 --exclude=__init__.py - - flake8 tests --ignore E402,F401,W503,E722 --max-line-length 220 + - flake8 MirahezeBots/plugins --max-line-length 265 --exclude=__init__.py + - flake8 tests --max-line-length 265 + - flake8 MirahezeBots/utils --max-line-length 265 --exclude=__init__.py - pytest tests/test_general.py - pytest tests/test_rss.py + - pytest tests/test_json.py - pip-missing-reqs --ignore-file=setup.py --ignore-module=pytest --ignore-module=MirahezeBots.* . - pip-extra-reqs . - pip check @@ -29,20 +29,16 @@ script: after_script: - pip freeze -jobs: - include: - - stage: deploy - python: 3.7 - deploy: - provider: pypi - username: __token__ - password: - secure: "s7+VjPrBDBICr3yvDEw9lq/iqE22BNYuNH3l6LL/5ebaQ+yqZ9hF0wWnd5JE8tdJnm2wXK5tHbIoIKr6quJLYYdZJ7MnuHdetYQFJjq8EQy3rU7LHcjho7nFU9I1b/RgQrLtFLx6Yq+Ozcceyux0+WWqfhoJ8Tptz1n03dOtdRupn+OMDSoJZLDAJqRrPseeVZpFKHFvitzbnPDYn5yAb2EW96wodYFYtJNZ4VFC5Gvh7qejbt7Cwtsu3KlPWubpYGBWnwMfyyM7yoV+gSQ/NvdFTtdbjH4Ebwy/DDKkwP9i9jNB5H5P0ntEIpDGvUhmbfj2G11rKHpLdAZsZgWqqLL74YsGTCmqoLVxNNDByWP8eN+U+u9uFY9D1fRbIshisl2yzTKkGV+dr9N+ZZNa9O8ii+emT9PpwCdWft9YrdsHuPNah3RkuuNede2jNeXpkBPd1t5sZvNtyto/CPRLNEavG8Z60yp+cXBNpam8FSBOK9ZAhc6DC7jPuX6hwPcNGwoZQra7wZMS457Pbpi7pfhjxApHhGSUikZ60+DvbEgDgi6Nut36M3SfS4FP/vRnJbIIb4k4I8o0X38Re4E4R5W5sD3TS7MACCRvOy/JNNmYnK0paxqgaUDZULDchdu3aU32mitDM8zC6ylDFVwLIpqkIaYmWjoD0CvNkpYh88U=" - -stages: - - name: deploy - if: branch = master AND type = push - +deploy: + provider: pypi + username: __token__ + password: + secure: "s7+VjPrBDBICr3yvDEw9lq/iqE22BNYuNH3l6LL/5ebaQ+yqZ9hF0wWnd5JE8tdJnm2wXK5tHbIoIKr6quJLYYdZJ7MnuHdetYQFJjq8EQy3rU7LHcjho7nFU9I1b/RgQrLtFLx6Yq+Ozcceyux0+WWqfhoJ8Tptz1n03dOtdRupn+OMDSoJZLDAJqRrPseeVZpFKHFvitzbnPDYn5yAb2EW96wodYFYtJNZ4VFC5Gvh7qejbt7Cwtsu3KlPWubpYGBWnwMfyyM7yoV+gSQ/NvdFTtdbjH4Ebwy/DDKkwP9i9jNB5H5P0ntEIpDGvUhmbfj2G11rKHpLdAZsZgWqqLL74YsGTCmqoLVxNNDByWP8eN+U+u9uFY9D1fRbIshisl2yzTKkGV+dr9N+ZZNa9O8ii+emT9PpwCdWft9YrdsHuPNah3RkuuNede2jNeXpkBPd1t5sZvNtyto/CPRLNEavG8Z60yp+cXBNpam8FSBOK9ZAhc6DC7jPuX6hwPcNGwoZQra7wZMS457Pbpi7pfhjxApHhGSUikZ60+DvbEgDgi6Nut36M3SfS4FP/vRnJbIIb4k4I8o0X38Re4E4R5W5sD3TS7MACCRvOy/JNNmYnK0paxqgaUDZULDchdu3aU32mitDM8zC6ylDFVwLIpqkIaYmWjoD0CvNkpYh88U=" + on: + python: "3.7" + branch: master + skip_existing: true + notifications: irc: channels: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1973c046..8ea1fb17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ **Changelog** Please see below for changes to MirahezeBot-Plugins +# Version 9.0.0 +## Miscellaneous +- travis: changed test configuration +- responses: corrections and style changes +- rss: style improvements +- test models: style tweaks +- test general: up'd max line length +- test rss: style changes & replaced http:// with https:// +## Requirements +- mwclient no longer required +- Setuptools bumped from 49.5.0 to 49.6.0 +- flake8 is now required for developers +- SQLAchemy is now at 1.3.19 +## Plugins +- all: removed future imports +- channelmgnt: switched to caching the json config +- channelmgnt: introduced a makemodechange system +- channelmgnt: added support_channel config +- status: replaced mwclient with a new util script, introduced cached json config, renamed other config +- phab: Introduced channel specific configuration + # Version 8.0.3 ## Miscellaneous diff --git a/MirahezeBots/plugins/adminlist.py b/MirahezeBots/plugins/adminlist.py index 13924b49..246455cf 100644 --- a/MirahezeBots/plugins/adminlist.py +++ b/MirahezeBots/plugins/adminlist.py @@ -1,11 +1,4 @@ -"""This module provides admin list and access level information.""" - -from __future__ import ( - unicode_literals, - absolute_import, - print_function, - division -) +"""This plugin provides admin list and access level information.""" from sopel.module import commands diff --git a/MirahezeBots/plugins/channelmgnt.py b/MirahezeBots/plugins/channelmgnt.py index f79d966b..9dce7012 100644 --- a/MirahezeBots/plugins/channelmgnt.py +++ b/MirahezeBots/plugins/channelmgnt.py @@ -7,31 +7,34 @@ Licensed under the Eiffel Forum License 2. https://sopel.chat """ -from __future__ import unicode_literals, absolute_import, print_function, division - import re import time -import json from sopel import formatting from sopel.module import ( - commands, example, priority, OP, require_chanmsg + commands, example, priority, OP, require_chanmsg, require_admin ) from sopel.config.types import StaticSection, ValidatedAttribute from sopel.tools import Identifier +from MirahezeBots.utils import jsonparser as jp +from sopel.tools import SopelMemory class ChannelmgntSection(StaticSection): datafile = ValidatedAttribute('datafile', str) + support_channel = ValidatedAttribute('support_channel', str) def setup(bot): bot.config.define_section('channelmgnt', ChannelmgntSection) + bot.memory["channelmgnt"] = SopelMemory() + bot.memory["channelmgnt"]["jdcache"] = jp.createdict(bot.settings.channelmgnt.datafile) def configure(config): config.define_section('channelmgnt', ChannelmgntSection, validate=False) config.channelmgnt.configure_setting('datafile', 'Where is the datafile for channelmgnt?') + config.channelmgnt.configure_setting('support_channel', 'What channel should users ask for help in?') def default_mask(trigger): @@ -43,19 +46,11 @@ def default_mask(trigger): return '{} {} {} {}'.format(welcome, chan, topic_, arg) -def fileread(file): - print(str(file)) - channellist = open(str(file), 'r') - chanopsjson = channellist.read() - channellist.close() - return chanopsjson - - def chanopget(channeldata, chanopsjson): chanops = [] if 'inherits-from' in channeldata.keys(): for x in channeldata["inherits-from"]: - y = channelparse(chanopsjson, x) + y = channelparse(channel=x, cachedjson=chanopsjson) chanops = chanops + y[0]["chanops"] if 'chanops' in channeldata.keys(): chanops = chanops + (channeldata["chanops"]) @@ -65,20 +60,16 @@ def chanopget(channeldata, chanopsjson): return chanops -def channelparse(chanopsjson, channel): - chanopsjsontemp = (json.loads(chanopsjson)) - if channel in chanopsjsontemp.keys(): - channeldata = chanopsjsontemp[channel] - return channeldata, chanopsjson +def channelparse(channel, cachedjson): + if channel in cachedjson.keys(): + channeldata = cachedjson[channel] + return channeldata, cachedjson else: return False -def get_chanops(bot, trigger): - file = bot.settings.channelmgnt.datafile - channel = str(trigger.sender) - chanopsjson = fileread(file) - channeldata = channelparse(chanopsjson, channel) +def get_chanops(channel, cachedjson): + channeldata = channelparse(channel=channel, cachedjson=cachedjson) if not channeldata: chanops = False else: @@ -86,29 +77,36 @@ def get_chanops(bot, trigger): return chanops -@require_chanmsg -@commands('chanmode') -@example('.chanmode +mz') -def chanmode(bot, trigger): - """ - Command to change channel mode. - """ - chanops = get_chanops(bot, trigger) +def makemodechange(bot, trigger, mode, isusermode=False, isbqmode=False): + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) if chanops: if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') + bot.say('Attempting to OP...') bot.say('op ' + trigger.sender, 'ChanServ') time.sleep(1) - modes = trigger.group(2) - channel = trigger.sender - if not modes: - bot.reply('Please specify what mode(s) to set') - if trigger.account in chanops: - bot.write(['MODE', channel, modes]) + if isusermode and not trigger.group(2): + bot.write(['MODE', trigger.sender, mode, trigger.nick]) + elif isusermode and trigger.account in chanops: + bot.write(['MODE', trigger.sender, mode, trigger.group(2)]) + elif isbqmode and trigger.account in chanops: + bot.write(['MODE', trigger.sender, mode, parse_host_mask(trigger.group().split())]) + elif trigger.account in chanops: + bot.write(['MODE', trigger.sender, mode]) else: bot.reply('Access Denied. If in error, please contact the channel founder.') + else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) + + +@require_chanmsg +@commands('chanmode') +@example('.chanmode +mz') +def chanmode(bot, trigger): + """ + Command to change channel mode. + """ + makemodechange(bot, trigger, trigger.group(2), isusermode=False) @require_chanmsg @@ -118,22 +116,7 @@ def op(bot, trigger): """ Command to op users in a room. If no nick is given, Sopel will op the nick who sent the command. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - nick = trigger.group(2) - channel = trigger.sender - if not nick: - nick = trigger.nick - if trigger.account in chanops: - bot.write(['MODE', channel, "+o", nick]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '+o', isusermode=True) @require_chanmsg @@ -143,22 +126,7 @@ def deop(bot, trigger): """ Command to deop users in a room. If no nick is given, Sopel will deop the nick who sent the command. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - nick = trigger.group(2) - channel = trigger.sender - if not nick: - bot.write(['MODE', channel, "-o", trigger.nick]) - elif trigger.account in chanops: - bot.write(['MODE', channel, "-o", nick]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '-o', isusermode=True) @require_chanmsg @@ -168,22 +136,7 @@ def voice(bot, trigger): """ Command to voice users in a room. If no nick is given, Sopel will voice the nick who sent the command. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - nick = trigger.group(2) - channel = trigger.sender - if not nick: - nick = trigger.nick - if trigger.account in chanops: - bot.write(['MODE', channel, "+v", nick]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '+v', isusermode=True) @require_chanmsg @@ -193,22 +146,7 @@ def devoice(bot, trigger): """ Command to devoice users in a room. If no nick is given, the nick who sent the command will be devoiced. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - nick = trigger.group(2) - channel = trigger.sender - if not nick: - bot.write(['MODE', channel, "-v", trigger.nick]) - elif trigger.account in chanops: - bot.write(['MODE', channel, "-v", nick]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '-v', isusermode=True) @require_chanmsg @@ -217,7 +155,7 @@ def devoice(bot, trigger): @example('.kick Zppix') def kick(bot, trigger): """Kick a user from the channel.""" - chanops = get_chanops(bot, trigger) + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) if chanops: if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: bot.say('Please wait...') @@ -243,10 +181,19 @@ def kick(bot, trigger): else: bot.reply('Access Denied. If in error, please contact the channel founder.') else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) -def configureHostMask(mask): +def parse_host_mask(text): + argc = len(text) + if argc < 2: + return + opt = Identifier(text[1]) + mask = opt + if not opt.is_nick(): + if argc < 3: + return + mask = text[2] if mask == '*!*@*': return mask if re.match('^[^.@!/]+$', mask) is not None: @@ -275,33 +222,7 @@ def configureHostMask(mask): def ban(bot, trigger): """Ban a user from the channel. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - text = trigger.group().split() - argc = len(text) - if argc < 2: - return - opt = Identifier(text[1]) - banmask = opt - channel = trigger.sender - if not opt.is_nick(): - if argc < 3: - return - channel = opt - banmask = text[2] - banmask = configureHostMask(banmask) - if banmask == '': - return - if trigger.account in chanops: - bot.write(['MODE', channel, '+b', banmask]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '+b', isbqmode=True) @require_chanmsg @@ -310,33 +231,7 @@ def ban(bot, trigger): def unban(bot, trigger): """Unban a user from the channel. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - text = trigger.group().split() - argc = len(text) - if argc < 2: - return - opt = Identifier(text[1]) - banmask = opt - channel = trigger.sender - if not opt.is_nick(): - if argc < 3: - return - channel = opt - banmask = text[2] - banmask = configureHostMask(banmask) - if banmask == '': - return - if trigger.account in chanops: - bot.write(['MODE', channel, '-b', banmask]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '-b', isbqmode=True) @require_chanmsg @@ -345,33 +240,7 @@ def unban(bot, trigger): def quiet(bot, trigger): """Quiet a user. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - text = trigger.group().split() - argc = len(text) - if argc < 2: - return - opt = Identifier(text[1]) - quietmask = opt - channel = trigger.sender - if not opt.is_nick(): - if argc < 3: - return - quietmask = text[2] - channel = opt - quietmask = configureHostMask(quietmask) - if quietmask == '': - return - if trigger.account in chanops: - bot.write(['MODE', channel, '+q', quietmask]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '+q', isbqmode=True) @require_chanmsg @@ -380,33 +249,7 @@ def quiet(bot, trigger): def unquiet(bot, trigger): """Unquiet a user. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) - if chanops: - if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: - bot.say('Please wait...') - bot.say('op ' + trigger.sender, 'ChanServ') - time.sleep(1) - text = trigger.group().split() - argc = len(text) - if argc < 2: - return - opt = Identifier(text[1]) - quietmask = opt - channel = trigger.sender - if not opt.is_nick(): - if argc < 3: - return - quietmask = text[2] - channel = opt - quietmask = configureHostMask(quietmask) - if quietmask == '': - return - if trigger.account in chanops: - bot.write(['MODE', channel, '-q', quietmask]) - else: - bot.reply('Access Denied. If in error, please contact the channel founder.') - else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + makemodechange(bot, trigger, '-q', isbqmode=True) @require_chanmsg @@ -416,7 +259,7 @@ def unquiet(bot, trigger): def kickban(bot, trigger): """Kick and ban a user from the channel. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) if chanops: if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: bot.say('Please wait...') @@ -441,7 +284,7 @@ def kickban(bot, trigger): mask = text[3] if any([s in text[3] for s in "!@*"]) else '' reasonidx = 4 if mask != '' else 3 reason = ' '.join(text[reasonidx:]) - mask = configureHostMask(mask) + mask = parse_host_mask(trigger.group().split()) if mask == '': mask = nick + '!*@*' if trigger.account in chanops: @@ -450,7 +293,7 @@ def kickban(bot, trigger): else: bot.reply('Access Denied. If in error, please contact the channel founder.') else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) @require_chanmsg @@ -459,7 +302,7 @@ def kickban(bot, trigger): def topic(bot, trigger): """Change the channel topic. The bot must be a channel operator for this command to work. """ - chanops = get_chanops(bot, trigger) + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) if chanops: if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: bot.say('Please wait...') @@ -469,7 +312,6 @@ def topic(bot, trigger): return channel = trigger.sender.lower() - narg = 1 mask = None mask = bot.db.get_channel_value(channel, 'topic_mask') mask = mask or default_mask(trigger) @@ -491,7 +333,7 @@ def topic(bot, trigger): else: bot.reply('Access Denied. If in error, please contact the channel founder.') else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) @require_chanmsg @@ -500,7 +342,7 @@ def topic(bot, trigger): def set_mask(bot, trigger): """Set the topic mask to use for the current channel. Within the topic mask, {} is used to allow substituting in chunks of text. This mask is used when running the 'topic' command. """ - chanops = get_chanops(bot, trigger) + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) if chanops: if trigger.account in chanops: bot.db.set_channel_value(trigger.sender, 'topic_mask', trigger.group(2)) @@ -508,7 +350,7 @@ def set_mask(bot, trigger): else: bot.reply('Access Denied. If in error, please contact the channel founder.') else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) @require_chanmsg @@ -527,8 +369,7 @@ def invite_user(bot, trigger): """ Command to invite users to a room. """ - chanops = get_chanops(bot, trigger) - nick = trigger.group(2) + chanops = get_chanops(str(trigger.sender), bot.memory["channelmgnt"]["jdcache"]) channel = trigger.sender if chanops: if bot.channels[trigger.sender].privileges[bot.nick] < OP and trigger.account in chanops: @@ -543,4 +384,28 @@ def invite_user(bot, trigger): else: bot.reply('Access Denied. If in error, please contact the channel founder.') else: - bot.reply('No ChanOps Found. Please ask for assistance in #miraheze-bots') + bot.reply('No ChanOps Found. Please ask for assistance in {}'.format(bot.settings.channelmgnt.support_channel)) + + +@require_admin(message="Only admins may purge cache.") +@commands('resetchanopcache') +def reset_chanop_cache(bot, trigger): + """ + Reset the cache of the channel management data file + """ + bot.reply("Refreshing Cache...") + bot.memory["channelmgnt"]["jdcache"] = jp.createdict(bot.settings.channelmgnt.datafile) + bot.reply("Cache refreshed") + + +@require_admin(message="Only admins may check cache") +@commands('checkchanopcache') +def check_chanop_cache(bot, trigger): + """ + Validate the cache matches the copy on disk + """ + result = jp.validatecache(bot.settings.channelmgnt.datafile, bot.memory["channelmgnt"]["jdcache"]) + if result: + bot.reply("Cache is correct.") + else: + bot.reply("Cache does not match on-disk copy") diff --git a/MirahezeBots/plugins/goofy.py b/MirahezeBots/plugins/goofy.py index 34283f10..34343dcd 100644 --- a/MirahezeBots/plugins/goofy.py +++ b/MirahezeBots/plugins/goofy.py @@ -1,10 +1,10 @@ """ Some commands for just goofing around and having fun """ -from sopel import module +from sopel.module import commands, example -@module.example('.coffee MirahezeBot') -@module.commands('coffee') +@example('.coffee MirahezeBot') +@commands('coffee') def coffee(bot, trigger): """ Makes me give the specified nick a coffee. @@ -15,8 +15,8 @@ def coffee(bot, trigger): bot.action("gives %s a nice warm cup of coffee." % (trigger.group(2)), trigger.sender) -@module.example('.hug MirahezeBot') -@module.commands('hug') +@example('.hug MirahezeBot') +@commands('hug') def hug(bot, trigger): """ Makes me give the specified nick a hug. @@ -27,8 +27,8 @@ def hug(bot, trigger): bot.action("gives %s a great big bear hug." % (trigger.group(2)), trigger.sender) -@module.example('.burger MirahezeBot') -@module.commands('burger') +@example('.burger MirahezeBot') +@commands('burger') def burger(bot, trigger): """ Makes me give the specified nick a burger. @@ -39,8 +39,8 @@ def burger(bot, trigger): bot.action("gives %s a freshly cooked cheeseburger." % (trigger.group(2)), trigger.sender) -@module.example('.present MirahezeBot') -@module.commands('present') +@example('.present MirahezeBot') +@commands('present') def present(bot, trigger): """ Makes me give the specified nick a present. @@ -51,8 +51,8 @@ def present(bot, trigger): bot.action("gives %s a present." % (trigger.group(2)), trigger.sender) -@module.example('.hotchoc MirahezeBot') -@module.commands('hotchoc', 'hotchocolate') +@example('.hotchoc MirahezeBot') +@commands('hotchoc', 'hotchocolate') def hotchoc(bot, trigger): """ Makes me give the specified nick a hot chocolate. diff --git a/MirahezeBots/plugins/joinall.py b/MirahezeBots/plugins/joinall.py index 40ad6cc0..6e3ff800 100644 --- a/MirahezeBots/plugins/joinall.py +++ b/MirahezeBots/plugins/joinall.py @@ -1,24 +1,16 @@ -"""This module implements .joinall.""" +"""This plugin implements .joinall.""" -from __future__ import ( - unicode_literals, - absolute_import, - print_function, - division -) - -from sopel import module +from sopel.module import commands, thread, require_admin import time -@module.require_admin -@module.commands('joinall') -@module.thread(True) +@require_admin +@commands('joinall') +@thread(True) def handle_joins(bot, trigger): """Join some channels.""" channels = bot.config.core.channels - if trigger.sender == '#ZppixBot': - for channel in channels: - bot.join(channel) - time.sleep(1) + for channel in channels: + bot.join(channel) + time.sleep(1) diff --git a/MirahezeBots/plugins/miraheze.py b/MirahezeBots/plugins/miraheze.py index 37f9744e..bf1ec899 100644 --- a/MirahezeBots/plugins/miraheze.py +++ b/MirahezeBots/plugins/miraheze.py @@ -1,11 +1,4 @@ -"""This module contains miraheze specific commands.""" -from __future__ import ( - unicode_literals, - absolute_import, - print_function, - division -) - +"""This plugin contains miraheze specific commands.""" from sopel.module import commands, example, rule MIRAHEZE_ABOUT_MIRAHEZE_CHANNEL = ( diff --git a/MirahezeBots/plugins/phab.py b/MirahezeBots/plugins/phab.py index edfeebc2..029a4ce4 100644 --- a/MirahezeBots/plugins/phab.py +++ b/MirahezeBots/plugins/phab.py @@ -1,22 +1,34 @@ """phab.by - Phabricator Task Information Plugin""" -import json # FIX THIS import requests # FIX THIS -from sopel.module import commands, example, interval, rule -from sopel.config.types import StaticSection, ValidatedAttribute -import sys +from sopel.module import commands, example, interval, rule, require_admin +from sopel.config.types import StaticSection, ValidatedAttribute, ListAttribute +from json import JSONDecodeError +from sopel.tools import get_logger, SopelMemory +from sopel.config import ConfigurationError +from MirahezeBots.utils import jsonparser as jp +from urllib.parse import urlparse +LOGGER = get_logger('phabricator') class PhabricatorSection(StaticSection): host = ValidatedAttribute('host', str) - api_token = ValidatedAttribute('api_token', str) - querykey = ValidatedAttribute('querykey', str) + api_token = ListAttribute('api_token', str) + querykey = ListAttribute('querykey', str) highpri_notify = ValidatedAttribute('highpri_notify', bool) highpri_channel = ValidatedAttribute('highpri_channel', str) + datafile = ValidatedAttribute('datafile', str) def setup(bot): bot.config.define_section('phabricator', PhabricatorSection) + if bot.settings.phabricator.host and bot.settings.phabricator.datafile: + raise ConfigurationError("Use of host and datafile together is not supported") + elif bot.settings.phabricator.host: + LOGGER.warn("Use of the host option was deceprated in 9.0.0 and will be removed in 10.0.0") + elif bot.settings.phabricator.datafile: + bot.memory["phab"] = SopelMemory() + bot.memory["phab"]["jdcache"] = jp.createdict(bot.settings.phabricator.datafile) def configure(config): @@ -27,6 +39,7 @@ def configure(config): config.phabricator.configure_setting('highpri_notify', 'Would you like to enable automatic notification of high priority tasks? (true/false)') config.phabricator.configure_setting('highpri_channel', 'If you enabled high priority notifications, what channel would you like them sent to? (notifications will be sent once every week.') + config.phabricator.configure_setting('datafile', 'File to read from to get channel specific data from') BOLD = '\x02' @@ -38,12 +51,25 @@ def configure(config): def searchphab(bot, channel, task=1): + if bot.settings.phabricator.host: + host = 'https://{0}/api'.format(bot.settings.phabricator.host) + apikey = bot.settings.phabricator.api_token[0] + elif bot.settings.phabricator.datafile: + if channel in bot.memory["phab"]["jdcache"]: + host = bot.memory["phab"]["jdcache"][str(channel)]["host"] + arraypos = int(bot.memory["phab"]["jdcache"][str(host)]["arraypos"]) + apikey = bot.settings.phabricator.api_token[int(arraypos)] + else: + host = bot.memory["phab"]["jdcache"]["default"]["host"] + arraypos = int(bot.memory["phab"]["jdcache"][str(host)]["arraypos"]) + apikey = bot.settings.phabricator.api_token[int(arraypos)] + data = { - 'api.token': bot.settings.phabricator.api_token, + 'api.token': apikey, 'constraints[ids][0]': task } response = requests.post( - url='https://{0}/api/maniphest.search'.format(bot.settings.phabricator.host), + url='{0}/maniphest.search'.format(host), data=data) response = response.json() go = 0 @@ -51,30 +77,30 @@ def searchphab(bot, channel, task=1): result = response.get("result").get("data")[0] go = 1 except AttributeError: - bot.say("An error occurred while parsing the result.", channel) + bot.say("An error occurred while parsing the result. ", channel) except IndexError: bot.say("Sorry, but I couldn't find information for the task you searched.", channel) - except: + except Exception: bot.say("An unknown error occured.", channel) if go == 1: params = { - 'api.token': bot.settings.phabricator.api_token, + 'api.token': apikey, 'constraints[phids][0]': result.get("fields").get("ownerPHID") } response2 = requests.post( - url='https://{0}/api/user.search'.format(bot.settings.phabricator.host), + url='{0}/user.search'.format(host), data=params) try: response2 = response2.json() - except json.decoder.JSONDecodeError as e: - bot.say(response2.text, '#ZppixBot-Logs') - bot.say(str(e), '#ZppixBot-Logs') + except JSONDecodeError as e: + bot.say(response2.text, bot.settings.core.logging_channel) + bot.say(str(e), bot.settings.core.logging_channel) params2 = { - 'api.token': bot.settings.phabricator.api_token, + 'api.token': apikey, 'constraints[phids][0]': result.get("fields").get("authorPHID") } response3 = requests.post( - url='https://{0}/api/user.search'.format(bot.settings.phabricator.host), + url='{0}/user.search'.format(host), data=params2) response3 = response3.json() if result.get("fields").get("ownerPHID") is None: @@ -84,7 +110,7 @@ def searchphab(bot, channel, task=1): author = response3.get("result").get("data")[0].get("fields").get("username") priority = result.get("fields").get("priority").get("name") status = result.get("fields").get("status").get("name") - output = 'https://phabricator.miraheze.org/T{0} - '.format(str(result["id"])) + output = '{0}/T{1} - '.format("https://" + str(urlparse(host).netloc), str(result["id"])) output = '{0}{2}{1}{2}, '.format(output, str(result.get('fields').get('name')), BOLD) output = output + 'authored by {1}{0}{1}, '.format(author, BOLD) output = output + 'assigned to {1}{0}{1}, '.format(owner, BOLD) @@ -94,19 +120,34 @@ def searchphab(bot, channel, task=1): def gethighpri(limit=True, channel='#miraheze', bot=None): + if bot.settings.phabricator.host: + host = 'https://{0}/api'.format(bot.settings.phabricator.host) + apikey = bot.settings.phabricator.api_token[0] + querykey = bot.settings.phabricator.querykey[0] + elif bot.settings.phabricator.datafile: + if channel in bot.memory["phab"]["jdcache"]: + host = bot.memory["phab"]["jdcache"][str(channel)]["host"] + arraypos = int(bot.memory["phab"]["jdcache"][str(host)]["arraypos"]) + apikey = bot.settings.phabricator.api_token[int(arraypos)] + querykey = bot.settings.phabricator.querykey[int(arraypos)] + else: + host = bot.memory["phab"]["jdcache"]["default"]["host"] + arraypos = int(bot.memory["phab"]["jdcache"][str(host)]["arraypos"]) + apikey = bot.settings.phabricator.api_token[int(arraypos)] + querykey = bot.settings.phabricator.querykey[int(arraypos)] data = { - 'api.token': bot.settings.phabricator.api_token, - 'queryKey': bot.settings.phabricator.querykey, # mFzMevK.KRMZ for mhphab + 'api.token': apikey, + 'queryKey': querykey, # mFzMevK.KRMZ for mhphab } response = requests.post( - url='https://{0}/api/maniphest.search'.format(bot.settings.phabricator.host), + url='{0}/maniphest.search'.format(host), data=data) response = response.json() result = response.get("result") try: data = result.get("data") go = 1 - except: + except Exception: bot.say("They are no high priority tasks that I can process, good job!", channel) go = 0 if go == 1: @@ -114,8 +155,7 @@ def gethighpri(limit=True, channel='#miraheze', bot=None): while x < len(data): currdata = data[x] if x > 5 and limit: - bot.say("They are more than 5 tasks. Please see {0} for the rest or use .highpri".format( - bot.settings.phabricator.host), channel) + bot.say("They are more than 5 tasks. Please see {0} for the rest or use .highpri".format(host), channel) break else: searchphab(bot=bot, channel=channel, task=currdata.get("id")) @@ -153,3 +193,27 @@ def high_priority_tasks_notification(bot): @example('.highpri') def forcehighpri(bot, trigger): gethighpri(limit=False, channel=trigger.sender, bot=bot) + + +@require_admin(message="Only admins may purge cache.") +@commands('resetphabcache') +def reset_phab_cache(bot, trigger): + """ + Reset the cache of the channel management data file + """ + bot.reply("Refreshing Cache...") + bot.memory["phab"]["jdcache"] = jp.createdict(bot.settings.phabricator.datafile) + bot.reply("Cache refreshed") + + +@require_admin(message="Only admins may check cache") +@commands('checkphabcache') +def check_phab_cache(bot, trigger): + """ + Validate the cache matches the copy on disk + """ + result = jp.validatecache(bot.settings.phabricator.datafile, bot.memory["phab"]["jdcache"]) + if result: + bot.reply("Cache is correct.") + else: + bot.reply("Cache does not match on-disk copy") diff --git a/MirahezeBots/plugins/pingpong.py b/MirahezeBots/plugins/pingpong.py index 78b393e9..6e634302 100644 --- a/MirahezeBots/plugins/pingpong.py +++ b/MirahezeBots/plugins/pingpong.py @@ -1,17 +1,10 @@ """ -ping.py - Sopel Ping Module. +ping.py - Sopel Ping Plugin. Author: Sean B. Palmer, inamidst.com About: http://sopel.chat """ -from __future__ import ( - unicode_literals, - absolute_import, - print_function, - division -) - from sopel.module import commands diff --git a/MirahezeBots/plugins/responses.py b/MirahezeBots/plugins/responses.py index 6878ebf8..5e793fd5 100644 --- a/MirahezeBots/plugins/responses.py +++ b/MirahezeBots/plugins/responses.py @@ -25,8 +25,7 @@ def addchan(bot, trigger): """Reply to channel request message.""" admins = ' '.join(map(str, bot.config.core.admin_accounts)) if bot.config.responses.support_channel is not None: - bot.say(("Hey {}, {} would like to have " - + "me in their channel: {}").format(admins, trigger.nick, trigger.group(2)), + bot.say(("Hey {}, {} would like to have me in their channel: {}").format(admins, trigger.nick, trigger.group(2)), bot.config.responses.support_channel) if trigger.sender != bot.config.responses.support_channel: bot.reply("Request sent! Action upon the request should be taken shortly. Thank you for using {}!".format(bot.nick)) @@ -55,12 +54,12 @@ def cancel(bot, trigger): @rate(user=2, channel=1, server=0) def botversion(bot, trigger): """List the current version of the bot.""" - bot.say('The current version of this bot is 8.0.3 (v8.0.3)') + bot.say('The current version of this bot is 9.0.0 (v9)') @commands('source', 'botsource') @example('.source') @rate(user=2, channel=1, server=0) def githubsource(bot, trigger): - """Give the link to ZppixBot's Github.""" + """Give the link to MirahezeBot's Github.""" bot.reply('My code can be found here: https://github.com/MirahezeBots/MirahezeBots') diff --git a/MirahezeBots/plugins/rss.py b/MirahezeBots/plugins/rss.py index cc9d56b2..8664f138 100644 --- a/MirahezeBots/plugins/rss.py +++ b/MirahezeBots/plugins/rss.py @@ -6,8 +6,7 @@ This module posts rss feed items to irc channels """ -from __future__ import unicode_literals -from sopel.config.types import StaticSection, ListAttribute, ValidatedAttribute +from sopel.config.types import StaticSection, ListAttribute from sopel.logger import get_logger from sopel.module import commands, interval, require_admin, example from sopel.tools import SopelMemory @@ -447,7 +446,6 @@ def _config_define(bot): bot.memory['rss'] = SopelMemory() bot.memory['rss']['feeds'] = dict() bot.memory['rss']['hashes'] = dict() - bot.memory['rss']['formats'] = dict() bot.memory['rss']['options'] = dict() bot.memory['rss']['formats'] = list() bot.memory['rss']['templates'] = dict() @@ -524,7 +522,7 @@ def _config_save(bot): bot.config.save() message = MESSAGES['saved_config_to_disk'] LOGGER.debug(message) - except BaseException: + except Exception: message = MESSAGES['unable_to_save_config_to_disk'] LOGGER.error(message) @@ -701,7 +699,7 @@ def _db_save_hash_to_database(bot, feedname, hash): bot.db.execute(sql_save_hashes, (hash,)) message = MESSAGES['saved_hash_of_feed_to_sqlite_table'].format(hash, feedname, tablename) LOGGER.debug(message) - except BaseException: + except Exception: message = MESSAGES['unable_to_save_hash_of_feed_to_sqlite_table'].format(hash, feedname, tablename) LOGGER.error(message) @@ -1555,7 +1553,7 @@ def get_feed(self): try: feed = feedparser.parse(self.url) return feed - except BaseException: + except Exception: return dict() def get_tinyurl(self, url): @@ -1577,7 +1575,7 @@ def get_feed(self): try: feed = feedparser.parse(self.url) return feed - except BaseException: + except Exception: return dict() def get_tinyurl(self, url): diff --git a/MirahezeBots/plugins/shortlinks.py b/MirahezeBots/plugins/shortlinks.py index 95e2512b..a8075a1a 100644 --- a/MirahezeBots/plugins/shortlinks.py +++ b/MirahezeBots/plugins/shortlinks.py @@ -1,8 +1,5 @@ -"""This module expands links to various websites""" -from __future__ import unicode_literals, absolute_import, print_function, division - -import re -from sopel.module import rule, commands, example +"""This plugin expands links to various websites""" +from sopel.module import commands, example @commands('github', 'gh') diff --git a/MirahezeBots/plugins/status.py b/MirahezeBots/plugins/status.py index d5d839a6..9af8dbb3 100644 --- a/MirahezeBots/plugins/status.py +++ b/MirahezeBots/plugins/status.py @@ -1,201 +1,117 @@ -from __future__ import unicode_literals, absolute_import, print_function, division +''' status.py - Mediawiki Status Page Updater ''' -import configparser -import json -import mwclient -import requests -import re -import time - -from mwclient import errors - -from sopel.module import rule, commands, example +from sopel.module import commands, example, require_admin from sopel.config.types import StaticSection, ValidatedAttribute +from MirahezeBots.utils import mwapihandler as mwapi +from MirahezeBots.utils import jsonparser as jp +from sopel.tools import SopelMemory pages = '' class StatusSection(StaticSection): - data_path = ValidatedAttribute('data_path', str) - wiki_username = ValidatedAttribute('wiki_username', str) - wiki_password = ValidatedAttribute('wiki_password', str) + datafile = ValidatedAttribute('datafile', str) + bot_username = ValidatedAttribute('bot_username', str) + bot_password = ValidatedAttribute('bot_password', str) support_channel = ValidatedAttribute('support_channel', str) def setup(bot): bot.config.define_section('status', StatusSection) + bot.memory["status"] = SopelMemory() + bot.memory["status"]["jdcache"] = jp.createdict(bot.settings.status.datafile) def configure(config): config.define_section('status', StatusSection, validate=False) - config.status.configure_setting('data_path', 'What is the path to the statusbot data files?') - config.status.configure_setting('wiki_username', 'What is the statusbot wiki username? (from Special:BotPasswords)') - config.status.configure_setting('wiki_password', 'What is the statusbot wiki password? (from Special:BotPasswords)') + config.status.configure_setting('datafile', 'What is the status data file?') + config.status.configure_setting('bot_username', 'What is the statusbot username? (from Special:BotPasswords)') + config.status.configure_setting('bot_password', "What is the statusbot accounts's bot password? (from Special:BotPasswords)") config.status.configure_setting('support_channel', 'Specify a support IRC channel (leave blank for none).') -def save_wrap(site, request, bot, trigger): - pagename = 'User:' + request[0] + '/Status' - bot.reply("Updating " + pagename + " to " + request[1] + "!") - page = site.Pages[pagename] - save_edit(page, request[1], bot, trigger) - - -def save_edit(page, status, bot, trigger): - time.sleep(5) - edit_summary = "BOT: Setting Status to: " + status + " per " \ - + trigger.hostmask - times = 0 - while True: - if times > 1: - break - try: - page.save(status, summary=edit_summary, bot=True, minor=True) - bot.reply("Updated!") - except errors.ProtectedPageError: - print('Could not edit ' + page + ' due to protection') - bot.reply("Error: Page Protected") - times += 1 - except errors.EditError: - print("Error") - bot.reply("An Error Occurred :(") - times += 1 - time.sleep(5) # sleep for 5 seconds before trying again - continue - except errors.UserBlocked: - bot.reply("StatusBot is currently unavailable for that wiki. Our team are working on it!") - bot.say("ERR: The bot is blocked on " + str(page), 'bot.config.core.logging_channel') - except requests.exceptions.Timeout: - bot.reply("We're experinecing delays " - + "connecting to that wiki. Try again in a few minutes.") - if bot.config.status.support_channel is not None: - bot.say("If this continues, let us know in {}".format(bot.config.status.support_channel)) - except requests.exceptions.TooManyRedirects as e: - bot.reply("We couldn't connect to that wiki.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Redirect error") - except requests.exceptions.ConnectionError as e: - bot.reply("We couldn't connect to that wiki.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Connection error") - except requests.exceptions.RequestException as e: - bot.reply("A fatal error occured.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Fatal error") - break - - -def main(bot, trigger, options): - cont = 0 - if len(options) == 2: - wiki = options[0] - status = options[1] - host = trigger.host - host = host.split('/') - cont = 1 - elif len(options) > 2: - wiki = options[0] - host = trigger.host - host = host.split('/') - status = options[1] - x = 2 - while x < len(options): - status = status + " " + options[x] - x = x + 1 - cont = 1 +def updatestatus(requestdata, authinfo, acldata, supportchan): + if requestdata[2] in acldata["wikis"].keys(): + wikiurl = str("https://" + acldata["wikis"][requestdata[2]]["url"] + "/w/api.php") + sulgroup = acldata["wikis"][requestdata[2]]["sulgroup"] else: - bot.reply("Syntax: .status wikicode status") - cont = 0 - if cont == 1: - cont = 0 - cloakfile = open(bot.config.status.data_path - + 'cloaks.csv', 'r') - for line in cloakfile: - auth = line.split(',') - if host[0] == auth[0]: - user = host[1] - sulgroup = auth[1] - wiki = [wiki, sulgroup] - request = [user, status] - cont = 1 - break - if cont == 0: - usersfile = open(bot.config.status.data_path - + 'users.csv', 'r') - for line in usersfile: - auth = line.split(',') - if str(trigger.account) == auth[0]: - user = auth[1] - sulgroup = auth[2] - wiki = [wiki, sulgroup] - request = [user, status] - cont = 1 - break - if cont == 0: - bot.reply("You don't seem to be authorised to use this module." - + " Please check you are signed into NickServ and try again.", trigger.sender) - if bot.config.status.support_channel is not None: - bot.say("If this persists, ask for help in {}".format(bot.config.status.support_channel)) - cont = 0 - if cont == 1: - wikiurl = 'example.org' - wikiexists = 0 - file = open(bot.config.status.data_path - + 'statuswikis.csv', 'r') - for line in file: - data = line.split(',') - if data[1] == wiki[0]: - wikiexists = 1 - if data[1] == wiki[0] and wiki[1] == data[2]: - wikiurl = data[0] - site = mwclient.Site((wikiurl), path='/w/') - try: - site.login(bot.config.status.wiki_username, - bot.config.status.wiki_password) - except errors.LoginError as e: - print(e) - raise ValueError("Login failed.") - except requests.exceptions.Timeout: - bot.reply("We're experinecing delays " - + "connecting to that wiki. Try again in a few minutes.") - if bot.config.status.support_channel is not None: - bot.say("If this continues, let us know in {}".format(bot.config.status.support_channel)) - except requests.exceptions.TooManyRedirects as e: - bot.reply("We couldn't connect to that wiki.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Redirect error") - except requests.exceptions.ConnectionError as e: - bot.reply("We couldn't connect to that wiki.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Connection error") - except requests.exceptions.RequestException as e: - bot.reply("A fatal error occured.") - if bot.config.status.support_channel is not None: - bot.say("I've alerted a maintainer in {}".format(bot.config.status.support_channel)) - print(e) - raise ValueError("Fatal error") - save_wrap(site, request, bot, trigger) - cont = 0 - if cont == 1 and wikiexists == 1: - bot.reply("I couldn't authentice you for that wiki.") - elif wikiexists == 0: - bot.reply("I don't recongise that wiki.") + return "Wiki could not be found" + if requestdata[0] in acldata["users"].keys(): + if sulgroup in acldata["users"][requestdata[0]]["groups"].keys(): + request = [acldata["users"][requestdata[0]]["groups"][sulgroup], requestdata[3]] + else: + return "Data not found for SULGROUP {} in {} - Keys were: {}".format(sulgroup, requestdata[0], acldata["users"][requestdata[0]].keys()) + elif requestdata[1][0] in acldata["sulgroups"][sulgroup]["cloaks"]: + request = [requestdata[1][1], requestdata[3]] + else: + message = "You don't seem to be authorised to use this plugin. Please check you are signed into NickServ and try again." + if supportchan is not None: + message = message + " If this persists, ask for help in {}".format(supportchan) + return message + content = mwapi.main(performer=request[0], target=str("User:" + (str(request[0]) + "/Status")), action="create", + reason=str("Updating status to " + str(request[1]) + " per " + str(request[0])), url=wikiurl, authinfo=[authinfo[0], authinfo[1]], content=str(request[1])) + return content @commands('status') @example('.status mhtest offline') def status(bot, trigger): """Update's the /Status subpage of Special:MyPage on the indicated wiki""" - options = trigger.group(2).split(" ") - main(bot, trigger, options) + options = [] + try: + options = trigger.group(2).split(" ") + if len(options) == 2: + wiki = options[0] + status = options[1] + host = trigger.host + host = host.split('/') + cont = 1 + elif len(options) > 2: + wiki = options[0] + host = trigger.host + host = host.split('/') + status = options[1] + x = 2 + while x < len(options): + status = status + " " + options[x] + x = x + 1 + cont = 1 + else: + bot.reply("Syntax: .status wikicode new-status") + cont = 0 + except AttributeError as e: + bot.reply("Syntax: .status wikicode new-status") + bot.say("AttributeError: {} from Status plugin in {}".format(e, trigger.sender), bot.config.core.logging_channel) + cont = 0 + if cont == 1: + requestdata = [str(trigger.account), host, wiki, str(status)] + response = updatestatus(requestdata, [bot.settings.status.bot_username, bot.settings.status.bot_password], bot.memory["status"]["jdcache"], bot.settings.status.support_channel) + if response == "create request sent. You may want to check the create log to be sure that it worked.": + bot.reply("Success") + else: + bot.reply(str(response)) + + +@require_admin(message="Only admins may purge cache.") +@commands('resetstatuscache') +def reset_status_cache(bot, trigger): + """ + Reset the cache of the channel management data file + """ + bot.reply("Refreshing Cache...") + bot.memory["status"]["jdcache"] = jp.createdict(bot.settings.status.datafile) + bot.reply("Cache refreshed") + + +@require_admin(message="Only admins may check cache") +@commands('checkstatuscache') +def check_status_cache(bot, trigger): + """ + Validate the cache matches the copy on disk + """ + result = jp.validatecache(bot.settings.status.datafile, bot.memory["status"]["jdcache"]) + if result: + bot.reply("Cache is correct.") + else: + bot.reply("Cache does not match on-disk copy") diff --git a/MirahezeBots/plugins/welcome.py b/MirahezeBots/plugins/welcome.py index 361ce3b6..fdf5fe10 100644 --- a/MirahezeBots/plugins/welcome.py +++ b/MirahezeBots/plugins/welcome.py @@ -1,11 +1,4 @@ -"""This module welcomes users upon joining the channel.""" - -from __future__ import ( - unicode_literals, - absolute_import, - print_function, - division -) +"""welcome.py - Plugin to welcome users upon joining the channel.""" import os import re @@ -17,26 +10,21 @@ CHANNEL_RE = re.compile(r'#[A-Za-z0-9#\-]+$') -def send_welcome(bot, trigger): - user = trigger.nick - if trigger.sender == '#miraheze' and user[:4] != 'Not-': +def send_welcome(nick, chan): + if chan == '#miraheze' and nick[:4] != 'Not-': message = ("Hello {}! If you have any questions, feel free to ask " - "and someone should answer soon.").format(trigger.nick) - elif trigger.sender == '#miraheze-cvt': + "and someone should answer soon.").format(nick) + elif chan == '#miraheze-cvt': message = ("Welcome {}. If you need to report spam or abuse," " please feel free to notify" " any of the voiced (+v) users," " if it contains personal information you can pm them," " or email us" - " at cvt [at] miraheze.org").format(trigger.nick) + " at cvt [at] miraheze.org").format(nick) else: - return - if trigger.account == '*': - bot.known_users_list[trigger.sender].append(trigger.nick) - else: - bot.known_users_list[trigger.sender].append(trigger.account) - bot.say(message) - save_known_users_list(get_filename(bot), bot.known_users_list) + message = None + + return message def get_filename(bot): @@ -91,10 +79,18 @@ def welcome_user(bot, trigger): bot.known_users_list[trigger.sender] = [] if trigger.account == '*': if trigger.nick not in bot.known_users_list[trigger.sender]: - send_welcome(bot, trigger) + bot.known_users_list[trigger.sender].append(trigger.nick) + welcome = send_welcome(trigger.nick, trigger.sender) + if welcome is not None: + bot.say(welcome) else: if trigger.account not in bot.known_users_list[trigger.sender] and trigger.nick not in bot.known_users_list[trigger.sender]: - send_welcome(bot, trigger) + bot.known_users_list[trigger.sender].append(trigger.account) + welcome = send_welcome(trigger.nick, trigger.sender) + if welcome is not None: + bot.say(welcome) + + save_known_users_list(get_filename(bot), bot.known_users_list) @commands('add_known', 'adduser') @@ -126,12 +122,12 @@ def add_known_user(bot, trigger): if username in bot.known_users_list[channel]: bot.say('{} is already added to known users list of channel {}'.format( - username, channel - )) + username, channel + )) return bot.known_users_list[channel].append(username) save_known_users_list(get_filename(bot), bot.known_users_list) bot.say('Okay, {} is now added to known users list of channel {}'.format( - username, channel - )) + username, channel + )) diff --git a/MirahezeBots/utils/__init__.py b/MirahezeBots/utils/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/MirahezeBots/utils/__init__.py @@ -0,0 +1 @@ + diff --git a/MirahezeBots/utils/jsonparser.py b/MirahezeBots/utils/jsonparser.py new file mode 100644 index 00000000..9d07415b --- /dev/null +++ b/MirahezeBots/utils/jsonparser.py @@ -0,0 +1,15 @@ +import json + + +def createdict(filename): + with open(filename) as jsonfile: + data = jsonfile.read() + return json.loads(data) + + +def validatecache(filename, dict): + cached = createdict(filename) + if cached == dict: + return True + else: + return False diff --git a/MirahezeBots/utils/mwapihandler.py b/MirahezeBots/utils/mwapihandler.py new file mode 100644 index 00000000..81869931 --- /dev/null +++ b/MirahezeBots/utils/mwapihandler.py @@ -0,0 +1,127 @@ +""" The functions in this file are not suitable for non-internal use. They are subject to change without notice and are not yet released. """ +import requests + + +def login(url, session, username='Example', password='password'): + CONNECTERRMSG = "Unable to conect to wiki" + PARAMS_0 = { + 'action': 'query', + 'meta': 'tokens', + 'type': 'login', + 'format': 'json', + } + try: + request = session.get(url=url, params=PARAMS_0) + DATA = request.json() + except Exception: + return ["Error", CONNECTERRMSG] + + LOGIN_TOKEN = DATA['query']['tokens']['logintoken'] + + PARAMS_1 = { + 'action': 'login', + 'lgname': username, + 'lgpassword': password, + 'lgtoken': LOGIN_TOKEN, + 'format': 'json', + } + try: + session.post(url, data=PARAMS_1) + except Exception: + return ["Error", CONNECTERRMSG] + return ["Success", "Logged in"] + + +def gettoken(url, session, type='csrftoken'): + PARAMS_2 = {'action': 'query', 'meta': 'tokens', 'format': 'json'} + + try: + request = session.get(url=url, params=PARAMS_2) + DATA = request.json() + except Exception: + return ["Error", "Unable to conect to wiki"] + + TOKEN = DATA['query']['tokens'][type] + return TOKEN + + +def makeaction(requestinfo, action, target, performer, reason, content=''): + if action == 'edit': + PARAMS = { + 'action': 'edit', + 'title': target, + 'summary': reason + ' (' + performer + ')', + 'appendtext': '\n* ' + performer + ': ' + reason, + 'token': requestinfo[2], + 'bot': 'true', + 'format': 'json', + } + elif action == "create": + PARAMS = { + 'action': 'edit', + 'title': target, + 'summary': reason, + 'text': content, + 'token': requestinfo[2], + 'bot': 'true', + 'format': 'json', + 'contentmodel': 'wikitext', + 'recreate': True, + 'watchlist': 'nochange', + } + + elif action == 'block': + PARAMS = { + 'action': 'block', + 'user': target, + 'expiry': 'infinite', + 'reason': 'Blocked by ' + performer + ' for ' + reason, + 'bot': 'false', + 'token': requestinfo[2], + 'format': 'json', + } + + elif action == 'unblock': + PARAMS = { + 'action': 'unblock', + 'user': target, + 'reason': 'Requested by ' + performer + ' Reason: ' + reason, + 'token': requestinfo[2], + 'format': 'json', + } + + elif action == 'delete': + PARAMS = { + 'action': 'delete', + 'title': target, + 'reason': 'Requested by ' + performer + ' Reason: ' + reason, + 'token': requestinfo[2], + 'format': 'json', + } + + try: + request = requestinfo[1].post(requestinfo[0], data=PARAMS) + DATA = request.json() + if DATA.get("error") is not None: + return ["MWError", (DATA.get("error").get("info"))] + else: + return ["Success", ("{} request sent. You may want to check the {} log to be sure that it worked.").format(action, action)] + except Exception: + return ["Fatal", ("An unexpected error occurred. Did you type the wiki or user incorrectly? Do I have {} rights on that wiki?").format(action)] + + +def main(performer, target, action, reason, url, authinfo, content=False): + session = requests.Session() + lg = login(url, session, authinfo[0], authinfo[1]) + if lg[0] == "Error": + return lg[1] + else: + TOKEN = gettoken(url, session, type='csrftoken') + if TOKEN[0] == "Error": + return TOKEN[1] + else: + if content: + act = makeaction([url, session, TOKEN], action, target, performer, reason, content) + else: + act = makeaction([url, session, TOKEN], action, target, performer, reason) + return act[1] diff --git a/dev-requirements.txt b/dev-requirements.txt index 0acc4afa..71a9638e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,5 +2,6 @@ vcrpy==4.1.0 six==1.15.0 pip-check-reqs==2.1.1 -setuptools==49.2.1 +setuptools==49.6.0 pytest==6.0.1 +flake8==3.8.3 diff --git a/requirements.txt b/requirements.txt index 0ab28b99..eb8aafc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ -# dependenices used in test ZppixBot +# dependenices used in test MirahezeBot feedparser==5.2.1 -mwclient==0.10.1 requests==2.24.0 sopel>=7.0.5,<8 -SQLAlchemy==1.3.18 +SQLAlchemy==1.3.19 diff --git a/setup.py b/setup.py index eccf93e7..5ea2fcb3 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,4 @@ from __future__ import print_function -import os -import sys from setuptools import setup, find_packages with open('README.md') as readme_file: @@ -17,7 +15,7 @@ setup( name='MirahezeBot_Plugins', - version='8.0.3', + version='9.0.0', description='Sopel Plugins for Miraheze Bots', long_description=readme + '\n\n' + history, long_description_content_type='text/markdown', # This is important! diff --git a/tests/models.py b/tests/models.py index fcc23ac5..4d02dd57 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,10 +1,7 @@ import sys -from sqlalchemy import Column, ForeignKey, Integer, String, create_engine -from sqlalchemy.orm import relationship +from sqlalchemy import Column, Integer, String, create_engine from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.schema import Sequence -import sqlalchemy.dialects.sqlite Base = declarative_base() diff --git a/tests/test_general.py b/tests/test_general.py index 805b795a..0aeff4e1 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -27,7 +27,7 @@ def test_db_schema_is_same(): def test_line_length(): - MAX_LENGTH = 220 + 1 + MAX_LENGTH = 265 + 1 for top, dirs, files in os.walk(PLUGINPATH): for filen in files: if not filen.endswith('.py'): diff --git a/tests/test_json.json b/tests/test_json.json new file mode 100644 index 00000000..c1ea9028 --- /dev/null +++ b/tests/test_json.json @@ -0,0 +1,17 @@ +{ + "#miraheze-cvt-private":{ + "inherits-from":[ + "#miraheze-cvt" + ] + }, + "#miraheze-cvt":{ + "chanops":[ + "NDKilla", + "Voidwalker", + "Reception123", + "The_Pionner", + "JohnLewis", + "Zppix" + ] + } +} diff --git a/tests/test_json.py b/tests/test_json.py new file mode 100644 index 00000000..f325d30f --- /dev/null +++ b/tests/test_json.py @@ -0,0 +1,20 @@ +from MirahezeBots.utils import jsonparser as jp + + +DICT = {'#miraheze-cvt-private': {'inherits-from': ['#miraheze-cvt']}, '#miraheze-cvt': {'chanops': ['NDKilla', 'Voidwalker', 'Reception123', 'The_Pionner', 'JohnLewis', 'Zppix']}} + +ALTDICT = {'#miraheze-cvt-fake': {'inherits-from': ['#miraheze-cvt']}, '#miraheze-cvt': {'chanops': ['NDKilla', 'Voidwalker', 'Reception123', 'The_Pionner', 'JohnLewis', 'Zppix']}} + + +def test_create_dict(): + assert DICT == jp.createdict('tests/test_json.json') + + +def test_check_dict_true(): + result = jp.validatecache('tests/test_json.json', DICT) + assert result is True + + +def test_check_dict_false(): + result = jp.validatecache('tests/test_json.json', ALTDICT) + assert result is False diff --git a/tests/test_rss.py b/tests/test_rss.py index 00da3d93..9929a95b 100644 --- a/tests/test_rss.py +++ b/tests/test_rss.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from sopel.db import SopelDB import MirahezeBots.plugins.rss as rss -from sopel.test_tools import MockSopel, MockConfig +from sopel.test_tools import MockSopel import hashlib import os import pytest @@ -10,69 +10,69 @@ import types FEED_VALID = ''' - + Site 1 Articles -http://www.site1.com/feed +https://www.site1.com/feed en Title 3 -http://www.site1.com/article3 +https://www.site1.com/article3 Description of article 3 Summary of article 3 Author 3 Sat, 23 Aug 2016 03:30:33 +0000 -3 at http://www.site1.com/ +3 at https://www.site1.com/ Title 2 -http://www.site1.com/article2 +https://www.site1.com/article2 Description of article 2 Summary of article 2 Author 2 Sat, 22 Aug 2016 02:20:22 +0000 -2 at http://www.site1.com/ +2 at https://www.site1.com/ Title 1 -http://www.site1.com/article1 +https://www.site1.com/article1 Description of article 1 Summary of article 1 Author 1 Sat, 21 Aug 2016 01:10:11 +0000 -1 at http://www.site1.com/ +1 at https://www.site1.com/ ''' FEED_BASIC = ''' - + Site 1 Articles -http://www.site1.com/feed +https://www.site1.com/feed Title 3 -http://www.site1.com/article3 +https://www.site1.com/article3 Title 2 -http://www.site1.com/article2 +https://www.site1.com/article2 Title 1 -http://www.site1.com/article1 +https://www.site1.com/article1 ''' FEED_INVALID = ''' - + Site 1 Articles -http://www.site1.com/feed +https://www.site1.com/feed en @@ -80,13 +80,13 @@ FEED_ITEM_NEITHER_TITLE_NOR_DESCRIPTION = ''' - + Site -http://www.site.com/feed +https://www.site.com/feed -http://www.site.com/article +https://www.site.com/article ''' @@ -96,14 +96,14 @@ News About <![CDATA[S&P Depository Receipts]]> - + News About en-us <![CDATA[Deutsche Bank Predicts 10% Pullback in S&P 500]]> - + - + @@ -150,14 +150,14 @@ def _fixture_bot_add_data(bot, id, url): @pytest.fixture(scope="function") def bot(request): bot = _fixture_bot_setup(request) - bot = _fixture_bot_add_data(bot, '1', 'http://www.site1.com/feed') + bot = _fixture_bot_add_data(bot, '1', 'https://www.site1.com/feed') return bot @pytest.fixture(scope="function") def bot_config_save(request): bot = _fixture_bot_setup(request) - bot = _fixture_bot_add_data(bot, '1', 'http://www.site1.com/feed') + bot = _fixture_bot_add_data(bot, '1', 'https://www.site1.com/feed') return bot @@ -170,8 +170,8 @@ def bot_basic(request): @pytest.fixture(scope="function") def bot_rss_list(request): bot = _fixture_bot_setup(request) - bot = _fixture_bot_add_data(bot, '1', 'http://www.site1.com/feed') - bot = _fixture_bot_add_data(bot, '2', 'http://www.site2.com/feed') + bot = _fixture_bot_add_data(bot, '1', 'https://www.site1.com/feed') + bot = _fixture_bot_add_data(bot, '2', 'https://www.site2.com/feed') return bot @@ -251,7 +251,7 @@ def test_rss_global_formats_feed(bot): bot.output = '' args = ['config', 'feeds'] rss._rss_config(bot, args) - expected = '#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'http://www.site1.com/feed' + rss.CONFIG_SEPARATOR + 'f=apl+atl\n' + expected = '#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'https://www.site1.com/feed' + rss.CONFIG_SEPARATOR + 'f=apl+atl\n' assert expected == bot.output @@ -259,7 +259,7 @@ def test_rss_global_get_post_feed_items(bot): rss._rss(bot, ['add', '#channel', 'feedname', FEED_VALID]) bot.output = '' rss._rss(bot, ['get', 'feedname']) - expected = '\x02[feedname]\x02 Title 1 \x02→\x02 http://www.site1.com/article1\n\x02[feedname]\x02 Title 2 \x02→\x02 http://www.site1.com/article2\n\x02[feedname]\x02 Title 3 \x02→\x02 http://www.site1.com/article3\n' # noqa: E501 + expected = '\x02[feedname]\x02 Title 1 \x02→\x02 https://www.site1.com/article1\n\x02[feedname]\x02 Title 2 \x02→\x02 https://www.site1.com/article2\n\x02[feedname]\x02 Title 3 \x02→\x02 https://www.site1.com/article3\n' # noqa: E501 assert expected == bot.output @@ -282,7 +282,7 @@ def test_rss_global_join(bot): def test_rss_global_list_feed(bot): rss._rss(bot, ['list', 'feed1']) - expected = '#channel1 feed1 http://www.site1.com/feed\n' + expected = '#channel1 feed1 https://www.site1.com/feed\n' assert expected == bot.output @@ -303,7 +303,7 @@ def test_rss_global_templates_get(bot): def test_rss_global_update_update(bot_rss_update): rss._rss(bot_rss_update, ['update']) - expected = '\x02[feed1]\x02 Title 1 \x02→\x02 http://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 http://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 http://www.site1.com/article3\n' + expected = '\x02[feed1]\x02 Title 1 \x02→\x02 https://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 https://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 https://www.site1.com/article3\n' assert expected == bot_rss_update.output @@ -340,7 +340,7 @@ def test_config_concatenate_channels(bot): def test_config_concatenate_feeds(bot, feedreader_feed_valid): bot.memory['rss']['options']['feed1'] = rss.Options(bot, feedreader_feed_valid, 'f=fy+fty') feeds = rss._config_concatenate_feeds(bot) - expected = ['#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'http://www.site1.com/feed' + rss.CONFIG_SEPARATOR + 'f=fy+fty'] + expected = ['#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'https://www.site1.com/feed' + rss.CONFIG_SEPARATOR + 'f=fy+fty'] assert expected == feeds @@ -427,7 +427,7 @@ def FIXME_test_config_save_writes(bot_config_save): db_filename = ''' + bot_config_save.db.filename + ''' channels = #channel1 [rss] -feeds = #channel1''' + rss.CONFIG_SEPARATOR + '''feed1''' + rss.CONFIG_SEPARATOR + '''http://www.site1.com/feed''' + rss.CONFIG_SEPARATOR + '''f=fl+ftl;t=t|>>{}<< +feeds = #channel1''' + rss.CONFIG_SEPARATOR + '''feed1''' + rss.CONFIG_SEPARATOR + '''https://www.site1.com/feed''' + rss.CONFIG_SEPARATOR + '''f=fl+ftl;t=t|>>{}<< formats = f=ft+ftpal templates = t=t|<<{}>> ''' @@ -717,13 +717,13 @@ def test_feed_list_format(bot): def test_feed_update_messages(bot, feedreader_feed_valid): rss._feed_update(bot, feedreader_feed_valid, 'feed1', True) - expected = '\x02[feed1]\x02 Title 1 \x02→\x02 http://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 http://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 http://www.site1.com/article3\n' + expected = '\x02[feed1]\x02 Title 1 \x02→\x02 https://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 https://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 https://www.site1.com/article3\n' assert expected == bot.output def test_feed_update_store_hashes(bot, feedreader_feed_valid): rss._feed_update(bot, feedreader_feed_valid, 'feed1', True) - expected = ['f3ec142344be7e04431001e0dc658ed0', '601daf484a5766ecff6f6d1dc19131dc', '53c674b8916ad03755a6f8b679515b3a'] + expected = ['9696cda86d9a337b37d1f4540c0f5d82', 'dc3088a287c801eb1087020dafee3d85', 'edc845b416110abf9a800552074cb415'] hashes = bot.memory['rss']['hashes']['feed1'].get() assert expected == hashes @@ -737,7 +737,7 @@ def test_feed_update_no_update(bot, feedreader_feed_valid): def test_hashes_read(bot, feedreader_feed_valid): rss._feed_update(bot, feedreader_feed_valid, 'feed1', True) - expected = ['f3ec142344be7e04431001e0dc658ed0', '601daf484a5766ecff6f6d1dc19131dc', '53c674b8916ad03755a6f8b679515b3a'] + expected = ['9696cda86d9a337b37d1f4540c0f5d82', 'dc3088a287c801eb1087020dafee3d85', 'edc845b416110abf9a800552074cb415'] bot.memory['rss']['hashes']['feed1'] = rss.RingBuffer(100) rss._hashes_read(bot, 'feed1') hashes = bot.memory['rss']['hashes']['feed1'].get() @@ -777,7 +777,7 @@ def test_rss_config_feeds_list(bot): bot.output = '' args = ['config', 'feeds'] rss._rss_config(bot, args) - expected = '#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'http://www.site1.com/feed' + rss.CONFIG_SEPARATOR + \ + expected = '#channel1' + rss.CONFIG_SEPARATOR + 'feed1' + rss.CONFIG_SEPARATOR + 'https://www.site1.com/feed' + rss.CONFIG_SEPARATOR + \ 'f=asl+als,#channel2' + rss.CONFIG_SEPARATOR + 'feed2' + rss.CONFIG_SEPARATOR + FEED_VALID + rss.CONFIG_SEPARATOR + 'f=p+tlpas\n' assert expected == bot.output @@ -984,16 +984,16 @@ def test_rss_formats_format_output(bot_rss_update): rss._rss_formats(bot_rss_update, ['format', 'feed1', 'f=fadglpst+fadglpst']) rss._rss_update(bot_rss_update, ['update']) expected = 'f=fadglpst+fadglpst' + ''' -\x02[feed1]\x02 Description of article 1 1 at http://www.site1.com/ \x02→\x02 http://www.site1.com/article1 (2016-08-21 01:10) Description of article 1 Title 1 -\x02[feed1]\x02 Description of article 2 2 at http://www.site1.com/ \x02→\x02 http://www.site1.com/article2 (2016-08-22 02:20) Description of article 2 Title 2 -\x02[feed1]\x02 Description of article 3 3 at http://www.site1.com/ \x02→\x02 http://www.site1.com/article3 (2016-08-23 03:30) Description of article 3 Title 3 +\x02[feed1]\x02 Description of article 1 1 at https://www.site1.com/ \x02→\x02 https://www.site1.com/article1 (2016-08-21 01:10) Description of article 1 Title 1 +\x02[feed1]\x02 Description of article 2 2 at https://www.site1.com/ \x02→\x02 https://www.site1.com/article2 (2016-08-22 02:20) Description of article 2 Title 2 +\x02[feed1]\x02 Description of article 3 3 at https://www.site1.com/ \x02→\x02 https://www.site1.com/article3 (2016-08-23 03:30) Description of article 3 Title 3 ''' assert expected == bot_rss_update.output def test_rss_formats_changes_are_saved(bot): rss._rss_formats(bot, ['format', 'feed1', 'f=asl+als']) - expected = ['#channel1;feed1;http://www.site1.com/feed;f=asl+als'] + expected = ['#channel1;feed1;https://www.site1.com/feed;f=asl+als'] assert expected == bot.config.rss.feeds @@ -1006,14 +1006,14 @@ def test_rss_get_feed_nonexistent(bot): def test_rss_get_post_feed_items(bot): rss._feed_add(bot, '#channel', 'feedname', FEED_VALID) rss._rss_get(bot, ['get', 'feedname']) - expected = '\x02[feedname]\x02 Title 1 \x02→\x02 http://www.site1.com/article1\n\x02[feedname]\x02 Title 2 \x02→\x02 http://www.site1.com/article2\n\x02[feedname]\x02 Title 3 \x02→\x02 http://www.site1.com/article3\n' # noqa: E501 + expected = '\x02[feedname]\x02 Title 1 \x02→\x02 https://www.site1.com/article1\n\x02[feedname]\x02 Title 2 \x02→\x02 https://www.site1.com/article2\n\x02[feedname]\x02 Title 3 \x02→\x02 https://www.site1.com/article3\n' # noqa: E501 assert expected == bot.output def test_rss_get_feed_spy(bot): rss._feed_add(bot, '#channel', 'SPY', FEED_SPY) rss._rss_get(bot, ['get', 'SPY']) - expected = '\x02[SPY]\x02 Deutsche Bank Predicts 10% Pullback in S&P 500 \x02→\x02 http://markets.financialcontent.com/stocks/news/read?GUID=32821698&Symbol=SPY\n' + expected = '\x02[SPY]\x02 Deutsche Bank Predicts 10% Pullback in S&P 500 \x02→\x02 https://markets.financialcontent.com/stocks/news/read?GUID=32821698&Symbol=SPY\n' assert expected == bot.output @@ -1070,21 +1070,21 @@ def test_rss_join(bot): def test_rss_list_all(bot_rss_list): rss._rss_list(bot_rss_list, ['list']) - expected1 = '#channel1 feed1 http://www.site1.com/feed' - expected2 = '#channel2 feed2 http://www.site2.com/feed' + expected1 = '#channel1 feed1 https://www.site1.com/feed' + expected2 = '#channel2 feed2 https://www.site2.com/feed' assert expected1 in bot_rss_list.output assert expected2 in bot_rss_list.output def test_rss_list_feed(bot): rss._rss_list(bot, ['list', 'feed1']) - expected = '#channel1 feed1 http://www.site1.com/feed\n' + expected = '#channel1 feed1 https://www.site1.com/feed\n' assert expected == bot.output def test_rss_list_channel(bot): rss._rss_list(bot, ['list', '#channel1']) - expected = '#channel1 feed1 http://www.site1.com/feed\n' + expected = '#channel1 feed1 https://www.site1.com/feed\n' assert expected == bot.output @@ -1131,19 +1131,19 @@ def test_rss_templates_override(bot): rss._rss_config(bot, ['config', 'templates', templates_default]) bot.output = '' rss._rss_get(bot, ['get', 'feed']) - expected = 'feedauthor:Author 1 addguid:1 at http://www.site1.com/ defaultpublished:2016-08-21 01:10 feedtitle:Title 1\nfeedauthor:Author 2 addguid:2 at http://www.site1.com/ defaultpublished:2016-08-22 02:20 feedtitle:Title 2\nfeedauthor:Author 3 addguid:3 at http://www.site1.com/ defaultpublished:2016-08-23 03:30 feedtitle:Title 3\n' # noqa: E501 + expected = 'feedauthor:Author 1 addguid:1 at https://www.site1.com/ defaultpublished:2016-08-21 01:10 feedtitle:Title 1\nfeedauthor:Author 2 addguid:2 at https://www.site1.com/ defaultpublished:2016-08-22 02:20 feedtitle:Title 2\nfeedauthor:Author 3 addguid:3 at https://www.site1.com/ defaultpublished:2016-08-23 03:30 feedtitle:Title 3\n' # noqa: E501 assert expected == bot.output def test_rss_templates_changes_are_saved(bot): rss._rss_templates(bot, ['format', 'feed1', 't=t|...{}...']) - expected = ['#channel1;feed1;http://www.site1.com/feed;t=t|...{}...'] + expected = ['#channel1;feed1;https://www.site1.com/feed;t=t|...{}...'] assert expected == bot.config.rss.feeds def test_rss_update_update(bot_rss_update): rss._rss_update(bot_rss_update, ['update']) - expected = '\x02[feed1]\x02 Title 1 \x02→\x02 http://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 http://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 http://www.site1.com/article3\n' + expected = '\x02[feed1]\x02 Title 1 \x02→\x02 https://www.site1.com/article1\n\x02[feed1]\x02 Title 2 \x02→\x02 https://www.site1.com/article2\n\x02[feed1]\x02 Title 3 \x02→\x02 https://www.site1.com/article3\n' assert expected == bot_rss_update.output