Plugin que scrapa o SICO

Signed-off-by: Pedro de Oliveira <falsovsky@gmail.com>
This commit is contained in:
Pedro de Oliveira 2021-04-29 20:37:24 +01:00
parent 333a821794
commit daa3b6cc7e
Signed by: falso
GPG Key ID: 1E4F05ACDBB2C85C
6 changed files with 336 additions and 0 deletions

1
Morte/README.md Normal file
View File

@ -0,0 +1 @@
SICO Client

71
Morte/__init__.py Normal file
View File

@ -0,0 +1,71 @@
###
# Copyright (c) 2021, Pedro de Oliveira
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
Morte: SICO Client
"""
import sys
import supybot
from supybot import world
# Use this for the version of this plugin.
__version__ = ""
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.authors.unknown
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {}
# This is a url where the most recent plugin package can be downloaded.
__url__ = ''
from . import config
from . import plugin
if sys.version_info >= (3, 4):
from importlib import reload
else:
from imp import reload
# In case we're being reloaded.
reload(config)
reload(plugin)
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
if world.testing:
from . import test
Class = plugin.Class
configure = config.configure
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

56
Morte/config.py Normal file
View File

@ -0,0 +1,56 @@
###
# Copyright (c) 2021, Pedro de Oliveira
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot import conf, registry
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('Morte')
except:
# Placeholder that allows to run the plugin on a bot
# without the i18n module
_ = lambda x: x
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified themself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Morte', True)
Morte = conf.registerPlugin('Morte')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(Morte, 'someConfigVariableName',
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

1
Morte/local/__init__.py Normal file
View File

@ -0,0 +1 @@
# Stub so local is a module, used for third-party modules

169
Morte/plugin.py Normal file
View File

@ -0,0 +1,169 @@
###
# Copyright (c) 2021, Pedro de Oliveira
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot import utils, plugins, ircutils, callbacks
from supybot.commands import *
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('Morte')
except ImportError:
# Placeholder that allows to run the plugin on a bot
# without the i18n module
_ = lambda x: x
from datetime import timedelta, datetime
import json
import re
import random
import requests
from requests.exceptions import Timeout
from bs4 import BeautifulSoup
class Morte(callbacks.Plugin):
"""SICO Client"""
threaded = True
timeout=5 # In minutes
fields = [
'< 1 ano',
'1-4 anos',
'5-17 anos',
'18-33 anos',
'34-69 anos',
'≥ 70 anos',
'Desconhecido',
]
regex = r"\"Atualizado a (\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\""
last = {}
def status(self, irc, msg, args):
"""
Returns the today death count from SICO.
"""
refresh=True
record={}
diff={
'< 1 ano': 0,
'1-4 anos': 0,
'5-17 anos': 0,
'18-33 anos': 0,
'34-69 anos': 0,
'≥ 70 anos': 0,
'Desconhecido': 0,
}
if len(self.last) > 0:
dt_old = self.last['dt'] + timedelta(minutes=self.timeout)
if dt_old > datetime.now():
refresh=False
if refresh:
try:
response = requests.get("https://evm.min-saude.pt/table?t=prematura&s=0", timeout=2)
except Timeout:
irc.error("9,14[SICO]9 {}".format("TIMEOUT"), prefixNick=True)
return
response.encoding="utf-8"
soup = BeautifulSoup(response.text, 'html.parser',)
javascript = soup.find('div', class_="active").find('script').string
values = json.loads(javascript)
try:
response = requests.get("https://evm.min-saude.pt/chart?t=main&s=0", timeout=2)
except Timeout:
irc.error("9,14[SICO]9 {}".format("TIMEOUT"), prefixNick=True)
return
response.encoding="utf-8"
match = re.search(self.regex, response.text)
if not match:
irc.error("9,14[SICO]9 {}".format("Parse error - no date"), prefixNick=True)
return
# last update date
up_dt = datetime(
year=int(match.group(3)),
month=int(match.group(2)),
day=int(match.group(1)),
hour=int(match.group(4)),
minute=int(match.group(5)),
second=int(match.group(6)),
)
dt = datetime.now()
for index, value in enumerate(values['x']['data'], start=-1):
if index < 0:
continue
key = self.fields[index]
value = int(value[-1])
record[key] = value
if len(self.last) > 0 and dt.strftime("%d") == self.last['dt'].strftime("%d"):
diff[key] = value - self.last['record'][key]
else:
dt = self.last['dt']
record = self.last['record']
up_dt = self.last['up_dt']
pipe=False
total=0
message = "9,14[SICO]9 {} ".format(up_dt.strftime("%Y-%m-%d"))
for key in record:
if record[key] == 0:
continue
total += record[key]
if pipe:
message += " | "
message += "{}: {}{}".format(
key,
record[key],
" (+{})".format(diff[key]) if diff[key] > 0 else ""
)
if key in ['< 1 ano', '1-4 anos'] and random.random() < 0.15:
message += " 13【"
pipe=True
if total > 0:
message += " | Total: {}".format(
total
)
else:
message += " ZERO!!!"
irc.reply(message, prefixNick=False)
if refresh:
self.last = {
'dt': dt,
'up_dt': up_dt,
'record': record
}
status = wrap(status, [])
Class = Morte
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

38
Morte/test.py Normal file
View File

@ -0,0 +1,38 @@
###
# Copyright (c) 2021, Pedro de Oliveira
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot.test import *
class MorteTestCase(PluginTestCase):
plugins = ('Morte',)
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: