diff --git a/CoronaLight/README.md b/CoronaLight/README.md new file mode 100644 index 0000000..199e3ff --- /dev/null +++ b/CoronaLight/README.md @@ -0,0 +1,13 @@ +Old version of plugin. Return the latest Coronavirus (COVID-19) statistics. Uses JHU data. + +[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=T8E56M6SP9JH2) + +`config plugins.coronalight.template` - Configure the template for replies + +Template default: `\x02$location: \x0307$confirmed\x03 infected, \x0304$dead\x03 dead ($ratio), \x0309$recovered\x03 recovered. (Last update: $updated)` + +`config plugins.coronalight.countryFirst` - Country name abbreviations take precedence over USA state name abbreviations when `True` + +countryFirst default: `False` + +`config plugins.coronalight.cacheLifetime` - Time in seconds to cache API results. Default: `600` diff --git a/CoronaLight/__init__.py b/CoronaLight/__init__.py new file mode 100644 index 0000000..01638db --- /dev/null +++ b/CoronaLight/__init__.py @@ -0,0 +1,66 @@ +### +# Copyright (c) 2020, Hoaas +# Copyright (c) 2020, oddluck +# 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. +### + +""" +CoronaLight: Return Corona virus information +""" + +import supybot +import supybot.world as world +import imp + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "2020.02.24+git" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.Author('oddluck', 'oddluck', 'oddluck@riseup.net') +__maintainer__ = {} + +# 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__ = 'https://github.com/oddluck/limnoria-plugins/' + +from . import config +from . import plugin +from imp import reload +reload(plugin) +reload(config) +# 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 diff --git a/CoronaLight/config.py b/CoronaLight/config.py new file mode 100644 index 0000000..21a8c7d --- /dev/null +++ b/CoronaLight/config.py @@ -0,0 +1,60 @@ +### +# Copyright (c) 2020, Hoaas +# Copyright (c) 2020, oddluck +# 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. +### + +import supybot.conf as conf +import supybot.registry as registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('CoronaLight') +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('CoronaLight', True) + + +CoronaLight = conf.registerPlugin('CoronaLight') + +conf.registerChannelValue(CoronaLight, 'template', + registry.String("\x02\x1F$location\x1F: Cases: \x0307$confirmed\x03 | Deaths: \x0304$dead\x03 (\x0304$ratio\x03) | Recovered: \x0309$recovered\x03 | Updated: $updated", _("""Template for replies"""))) + +conf.registerChannelValue(CoronaLight, 'countryFirst', + registry.Boolean(False, _("Give preference to country name abbreviations over USA state name abbreviations"))) + +conf.registerGlobalValue(CoronaLight, 'cacheLifetime', + registry.Integer(600, _("Amount of time in seconds to cache API results"))) diff --git a/CoronaLight/plugin.py b/CoronaLight/plugin.py new file mode 100644 index 0000000..b5b9abd --- /dev/null +++ b/CoronaLight/plugin.py @@ -0,0 +1,569 @@ +### +# Copyright (c) 2020, Hoaas +# Copyright (c) 2020, oddluck +# 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. +### + +import json +import requests +import csv +import datetime +from supybot import utils, plugins, ircutils, callbacks, log +from supybot.commands import * +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('CoronaLight') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + +countries = { + "AF": "AFGHANISTAN", + "AX": "ÅLAND ISLANDS", + "AL": "ALBANIA", + "DZ": "ALGERIA", + "AS": "AMERICAN SAMOA", + "AD": "ANDORRA", + "AO": "ANGOLA", + "AI": "ANGUILLA", + "AQ": "ANTARCTICA", + "AG": "ANTIGUA AND BARBUDA", + "AR": "ARGENTINA", + "AM": "ARMENIA", + "AW": "ARUBA", + "AU": "AUSTRALIA", + "AT": "AUSTRIA", + "AZ": "AZERBAIJAN", + "BS": "BAHAMAS, THE", + "BH": "BAHRAIN", + "BD": "BANGLADESH", + "BB": "BARBADOS", + "BY": "BELARUS", + "BE": "BELGIUM", + "BZ": "BELIZE", + "BJ": "BENIN", + "BM": "BERMUDA", + "BT": "BHUTAN", + "BO": "BOLIVIA", + "BQ": "BONAIRE", + "BA": "BOSNIA AND HERZEGOVINA", + "BW": "BOTSWANA", + "BV": "BOUVET ISLAND", + "BR": "BRAZIL", + "IO": "BRITISH INDIAN OCEAN TERRITORY", + "BN": "BRUNEI", + "BG": "BULGARIA", + "BF": "BURKINA FASO", + "BI": "BURUNDI", + "KH": "CAMBODIA", + "CM": "CAMEROON", + "CA": "CANADA", + "CV": "CAPE VERDE", + "KY": "CAYMAN ISLANDS", + "CF": "CENTRAL AFRICAN REPUBLIC", + "TD": "CHAD", + "CL": "CHILE", + "CN": "CHINA", + "CX": "CHRISTMAS ISLAND", + "CC": "COCOS ISLANDS", + "CO": "COLOMBIA", + "KM": "COMOROS", + "CG": "CONGO (BRAZZAVILLE)", + "CD": "CONGO (KINSHASA)", + "CK": "COOK ISLANDS", + "CR": "COSTA RICA", + "CI": "CÔTE D'IVOIRE", + "HR": "CROATIA", + "CU": "CUBA", + "CW": "CURAÇAO", + "CY": "CYPRUS", + "CZ": "CZECHIA", + "DK": "DENMARK", + "DJ": "DJIBOUTI", + "DM": "DOMINICA", + "DO": "DOMINICAN REPUBLIC", + "EC": "ECUADOR", + "EG": "EGYPT", + "SV": "EL SALVADOR", + "GQ": "EQUATORIAL GUINEA", + "ER": "ERITREA", + "EE": "ESTONIA", + "ET": "ETHIOPIA", + "FK": "FALKLAND ISLANDS", + "FO": "FAROE ISLANDS", + "FJ": "FIJI", + "FI": "FINLAND", + "FR": "FRANCE", + "GF": "GUIANA", + "PF": "POLYNESIA", + "TF": "FRENCH SOUTHERN TERRITORIES", + "GA": "GABON", + "GM": "GAMBIA, THE", + "GE": "GEORGIA", + "DE": "GERMANY", + "GH": "GHANA", + "GI": "GIBRALTAR", + "GR": "GREECE", + "GL": "GREENLAND", + "GD": "GRENADA", + "GP": "GUADELOUPE", + "GU": "GUAM", + "GT": "GUATEMALA", + "GG": "GUERNSEY", + "GN": "GUINEA", + "GW": "GUINEA-BISSAU", + "GY": "GUYANA", + "HT": "HAITI", + "HM": "HEARD ISLAND AND MCDONALD ISLANDS", + "VA": "HOLY SEE", + "HN": "HONDURAS", + "HK": "HONG KONG", + "HU": "HUNGARY", + "IS": "ICELAND", + "IN": "INDIA", + "ID": "INDONESIA", + "IR": "IRAN", + "IQ": "IRAQ", + "IE": "IRELAND", + "IM": "ISLE OF MAN", + "IL": "ISRAEL", + "IT": "ITALY", + "JM": "JAMAICA", + "JP": "JAPAN", + "JE": "JERSEY", + "JO": "JORDAN", + "KZ": "KAZAKHSTAN", + "KE": "KENYA", + "KI": "KIRIBATI", + "KP": "KOREA, NORTH", + "KR": "KOREA, SOUTH", + "KW": "KUWAIT", + "KG": "KYRGYZSTAN", + "LA": "LAOS", + "LV": "LATVIA", + "LB": "LEBANON", + "LS": "LESOTHO", + "LR": "LIBERIA", + "LY": "LIBYA", + "LI": "LIECHTENSTEIN", + "LT": "LITHUANIA", + "LU": "LUXEMBOURG", + "MO": "MACAO", + "MK": "NORTH MACEDONIA", + "MG": "MADAGASCAR", + "MW": "MALAWI", + "MY": "MALAYSIA", + "MV": "MALDIVES", + "ML": "MALI", + "MT": "MALTA", + "MH": "MARSHALL ISLANDS", + "MQ": "MARTINIQUE", + "MR": "MAURITANIA", + "MU": "MAURITIUS", + "YT": "MAYOTTE", + "MX": "MEXICO", + "FM": "MICRONESIA", + "MD": "MOLDOVA", + "MC": "MONACO", + "MN": "MONGOLIA", + "ME": "MONTENEGRO", + "MS": "MONTSERRAT", + "MA": "MOROCCO", + "MZ": "MOZAMBIQUE", + "MM": "MYANMAR", + "NA": "NAMIBIA", + "NR": "NAURU", + "NP": "NEPAL", + "NL": "NETHERLANDS", + "NC": "NEW CALEDONIA", + "NZ": "NEW ZEALAND", + "NI": "NICARAGUA", + "NE": "NIGER", + "NG": "NIGERIA", + "NU": "NIUE", + "NF": "NORFOLK ISLAND", + "MP": "NORTHERN MARIANA ISLANDS", + "NO": "NORWAY", + "OM": "OMAN", + "PK": "PAKISTAN", + "PW": "PALAU", + "PS": "PALESTINE, STATE OF", + "PA": "PANAMA", + "PG": "PAPUA NEW GUINEA", + "PY": "PARAGUAY", + "PE": "PERU", + "PH": "PHILIPPINES", + "PN": "PITCAIRN", + "PL": "POLAND", + "PT": "PORTUGAL", + "PR": "PUERTO RICO", + "QA": "QATAR", + "RE": "RÉUNION", + "RO": "ROMANIA", + "RU": "RUSSIA", + "RW": "RWANDA", + "BL": "SAINT BARTHÉLEMY", + "SH": "SAINT HELENA", + "KN": "SAINT KITTS AND NEVIS", + "LC": "SAINT LUCIA", + "MF": "SAINT MARTIN", + "PM": "SAINT PIERRE AND MIQUELON", + "VC": "SAINT VINCENT AND THE GRENADINES", + "WS": "SAMOA", + "SM": "SAN MARINO", + "ST": "SAO TOME AND PRINCIPE", + "SA": "SAUDI ARABIA", + "SN": "SENEGAL", + "RS": "SERBIA", + "SC": "SEYCHELLES", + "SL": "SIERRA LEONE", + "SG": "SINGAPORE", + "SX": "SINT MAARTEN", + "SK": "SLOVAKIA", + "SI": "SLOVENIA", + "SB": "SOLOMON ISLANDS", + "SO": "SOMALIA", + "ZA": "SOUTH AFRICA", + "GS": "GEORGIA", + "SS": "SOUTH SUDAN", + "ES": "SPAIN", + "LK": "SRI LANKA", + "SD": "SUDAN", + "SR": "SURINAME", + "SJ": "SVALBARD AND JAN MAYEN", + "SZ": "SWAZILAND", + "SE": "SWEDEN", + "CH": "SWITZERLAND", + "SY": "SYRIA", + "TW": "TAIWAN", + "TJ": "TAJIKISTAN", + "TZ": "TANZANIA", + "TH": "THAILAND", + "TL": "TIMOR-LESTE", + "TG": "TOGO", + "TK": "TOKELAU", + "TO": "TONGA", + "TT": "TRINIDAD AND TOBAGO", + "TN": "TUNISIA", + "TR": "TURKEY", + "TM": "TURKMENISTAN", + "TC": "TURKS AND CAICOS ISLANDS", + "TV": "TUVALU", + "UG": "UGANDA", + "UA": "UKRAINE", + "AE": "UNITED ARAB EMIRATES", + "GB": "UNITED KINGDOM", + "UK": "UNITED KINGDOM", + "US": "US", + "UM": "UNITED STATES MINOR OUTLYING ISLANDS", + "UY": "URUGUAY", + "UZ": "UZBEKISTAN", + "VU": "VANUATU", + "VE": "VENEZUELA", + "VN": "VIETNAM", + "VG": "VIRGIN ISLANDS", + "VI": "VIRGIN ISLANDS", + "WF": "WALLIS AND FUTUNA", + "EH": "WESTERN SAHARA", + "YE": "YEMEN", + "ZM": "ZAMBIA", + "ZW": "ZIMBABWE" +} + +states = { + 'AK': 'ALASKA', + 'AL': 'ALABAMA', + 'AR': 'ARKANSAS', + 'AS': 'AMERICAN SAMOA', + 'AZ': 'ARIZONA', + 'CA': 'CALIFORNIA', + 'CO': 'COLORADO', + 'CT': 'CONNECTICUT', + 'DC': 'DISTRICT OF COLUMBIA', + 'DE': 'DELAWARE', + 'FL': 'FLORIDA', + 'GA': 'GEORGIA', + 'GU': 'GUAM', + 'HI': 'HAWAII', + 'IA': 'IOWA', + 'ID': 'IDAHO', + 'IL': 'ILLINOIS', + 'IN': 'INDIANA', + 'KS': 'KANSAS', + 'KY': 'KENTUCKY', + 'LA': 'LOUISIANA', + 'MA': 'MASSACHUSETTS', + 'MD': 'MARYLAND', + 'ME': 'MAINE', + 'MI': 'MICHIGAN', + 'MN': 'MINNESOTA', + 'MO': 'MISSOURI', + 'MP': 'NORTHERN MARIANA ISLANDS', + 'MS': 'MISSISSIPPI', + 'MT': 'MONTANA', + 'NA': 'NATIONAL', + 'NC': 'NORTH CAROLINA', + 'ND': 'NORTH DAKOTA', + 'NE': 'NEBRASKA', + 'NH': 'NEW HAMPSHIRE', + 'NJ': 'NEW JERSEY', + 'NM': 'NEW MEXICO', + 'NV': 'NEVADA', + 'NY': 'NEW YORK', + 'OH': 'OHIO', + 'OK': 'OKLAHOMA', + 'OR': 'OREGON', + 'PA': 'PENNSYLVANIA', + 'PR': 'PUERTO RICO', + 'RI': 'RHODE ISLAND', + 'SC': 'SOUTH CAROLINA', + 'SD': 'SOUTH DAKOTA', + 'TN': 'TENNESSEE', + 'TX': 'TEXAS', + 'UT': 'UTAH', + 'VA': 'VIRGINIA', + 'VI': 'VIRGIN ISLANDS', + 'VT': 'VERMONT', + 'WA': 'WASHINGTON', + 'WI': 'WISCONSIN', + 'WV': 'WEST VIRGINIA', + 'WY': 'WYOMING' +} + +class CoronaLight(callbacks.Plugin): + """Displays current stats of the Coronavirus outbreak""" + threaded = True + + def __init__(self, irc): + self.__parent = super(CoronaLight, self) + self.__parent.__init__(irc) + self.cache = {} + + def getCSV(self): + data = None + try: + day = datetime.date.today().strftime('%m-%d-%Y') + url = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/{0}.csv".format(day) + r = requests.get(url, timeout=10) + r.raise_for_status() + except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e: + log.debug('Corona: error retrieving data for today: {0}'.format(e)) + try: + day = datetime.date.today() - datetime.timedelta(days=1) + day = day.strftime('%m-%d-%Y') + url = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/{0}.csv".format(day) + r = requests.get(url, timeout=10) + r.raise_for_status() + except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e: + log.debug('Corona: error retrieving data for yesterday: {0}'.format(e)) + else: + data = csv.DictReader(r.iter_lines(decode_unicode = True)) + else: + data = csv.DictReader(r.iter_lines(decode_unicode = True)) + return data + + def getAPI(self): + data = None + url = "https://services1.arcgis.com/0MSEUqKaxRlEPj5g/arcgis/rest/services/ncov_cases/FeatureServer/1/query?f=json&where=Confirmed>0&outFields=*" + try: + r = requests.get(url, timeout=10) + r.raise_for_status() + except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e: + log.debug('Corona: error retrieving data from API: {0}'.format(e)) + else: + try: + r = json.loads(r.content.decode()) + data = r.get('features') + except: + data = None + if not data: + log.debug("Corona: Error retrieving features data from API.") + return data + + def timeCreated(self, time): + time = datetime.datetime.fromtimestamp(time/1000.0) + d = datetime.datetime.now() - time + if d.days: + rel_time = "{:1d}d ago".format(abs(d.days)) + elif d.seconds > 3600: + rel_time = "{:.1f}h ago".format(round((abs(d.seconds) / 3600),1)) + elif 60 <= d.seconds < 3600: + rel_time = "{:.1f}m ago".format(round((abs(d.seconds) / 60),1)) + else: + rel_time = "%ss ago" % (abs(d.seconds)) + return rel_time + + @wrap([optional('text')]) + def corona(self, irc, msg, args, search): + """[region] + Displays Coronavirus statistics. Add a region name to search for country/state + specific results. Accepts full country/state names or ISO 3166-1 alpha-2 (two + character) country abbreviations and US Postal (two character) state abbreviations. + Invalid region names or search terms without data return global results. + """ + git = api = False + data = None + if len(self.cache) > 0: + cacheLifetime = self.registryValue("cacheLifetime") + now = datetime.datetime.now() + seconds = (now - self.cache['timestamp']).total_seconds() + if seconds < cacheLifetime: + data = self.cache['data'] + api = True + log.debug("Corona: returning cached API data") + else: + data = self.getAPI() + if data: + api = True + self.cache['timestamp'] = datetime.datetime.now() + self.cache['data'] = data + log.debug("Corona: caching API data") + else: + now = datetime.datetime.now() + midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) + seconds = (now - midnight).seconds + if seconds > (now - self.cache['timestamp']).total_seconds(): + data = self.cache['data'] + api = True + log.debug("Corona: error accessing API, returning cached API data") + else: + data = self.getCSV() + if data: + git = True + else: + data = self.getAPI() + if data: + api = True + self.cache['timestamp'] = datetime.datetime.now() + self.cache['data'] = data + log.debug("Corona: caching API data") + else: + data = self.getCSV() + if data: + git = True + if not data: + irc.reply("Error. Unable to access database.") + return + total_confirmed = total_deaths = total_recovered = total_active = last_update = 0 + confirmed = deaths = recovered = active = updated = 0 + location = 'Global' + for region in data: + if api: + r = region.get('attributes') + else: + r = region + if search: + if api: + region = r.get('Country_Region') + state = r.get('Province_State') + else: + region = r.get('Country/Region') + state = r.get('Province/State') + if len(search) == 2: + if self.registryValue("countryFirst", msg.channel): + try: + search = countries[search.upper()] + except KeyError: + try: + search = states[search.upper()] + except KeyError: + pass + else: + try: + search = states[search.upper()] + except KeyError: + try: + search = countries[search.upper()] + except KeyError: + pass + if search.lower() == 'usa' or 'united states' in search.lower(): + search = 'us' + if 'korea' in search.lower(): + search = 'korea, south' + if region and search.lower() == region.lower(): + location = region + confirmed += int(r.get('Confirmed')) + deaths += int(r.get('Deaths')) + recovered += int(r.get('Recovered')) + active += int(r.get('Active')) + if api: + time = int(r.get('Last_Update')) + if git: + time = datetime.datetime.strptime(r.get('Last Update'), "%Y-%m-%dT%H:%M:%S") + time = int(time.timestamp()*1000) + if time > updated: + updated = time + local_ratio_dead = "{0:.1%}".format(deaths/confirmed) + elif state and search.lower() == state.lower(): + location = state + confirmed += int(r.get('Confirmed')) + deaths += int(r.get('Deaths')) + recovered += int(r.get('Recovered')) + active += int(r.get('Active')) + if api: + time = int(r.get('Last_Update')) + if git: + time = datetime.datetime.strptime(r.get('Last Update'), "%Y-%m-%dT%H:%M:%S") + time = int(time.timestamp()*1000) + if time > updated: + updated = time + local_ratio_dead = "{0:.1%}".format(deaths/confirmed) + total_confirmed += int(r.get('Confirmed')) + total_deaths += int(r.get('Deaths')) + total_recovered += int(r.get('Recovered')) + total_active += int(r.get('Active')) + if api: + time = int(r.get('Last_Update')) + if git: + time = datetime.datetime.strptime(r.get('Last Update'), "%Y-%m-%dT%H:%M:%S") + time = int(time.timestamp()*1000) + if time > last_update: + last_update = time + ratio_dead = "{0:.1%}".format(total_deaths/total_confirmed) + template = self.registryValue("template", msg.channel) + if location == 'Global': + last_update = self.timeCreated(last_update) + template = template.replace("$location", location) + template = template.replace("$confirmed", str(total_confirmed)) + template = template.replace("$dead", str(total_deaths)) + template = template.replace("$recovered", str(total_recovered)) + template = template.replace("$ratio", ratio_dead) + template = template.replace("$active", str(total_active)) + template = template.replace("$updated", last_update) + else: + updated = self.timeCreated(updated) + template = template.replace("$location", location) + template = template.replace("$confirmed", str(confirmed)) + template = template.replace("$dead", str(deaths)) + template = template.replace("$recovered", str(recovered)) + template = template.replace("$ratio", local_ratio_dead) + template = template.replace("$active", str(active)) + template = template.replace("$updated", updated) + irc.reply(template) + +Class = CoronaLight diff --git a/CoronaLight/requirements.txt b/CoronaLight/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/CoronaLight/requirements.txt @@ -0,0 +1 @@ +requests