171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
'''
|
|
This file is part of JANINE.
|
|
|
|
JANINE is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
JANINE is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with JANINE. If not, see <https://www.gnu.org/licenses/>.
|
|
'''
|
|
import logging
|
|
import asyncio
|
|
from urllib3.exceptions import MaxRetryError
|
|
|
|
from mira.module import BaseModule
|
|
from mira.modules.janine.sources import MiscDataSources, WarningSources
|
|
from mira.modules.janine.warnings import parse_data, stub_warning
|
|
from mira.modules.janine.helpers import format_warning
|
|
|
|
import requests
|
|
import aioxmpp
|
|
|
|
logger = logging.getLogger('mira.modules.janine.janine')
|
|
|
|
NAME = 'janine'
|
|
|
|
class JanineModule(BaseModule):
|
|
__instance = None
|
|
|
|
@staticmethod
|
|
def get_instance(base, **kwargs):
|
|
if JanineModule.__instance == None:
|
|
JanineModule(base, **kwargs)
|
|
|
|
return JanineModule.__instance
|
|
|
|
def __init__(self, base, **kwargs):
|
|
if JanineModule.__instance != None:
|
|
raise Exception('Trying to init singleton twice')
|
|
|
|
super().__init__(base, **kwargs)
|
|
JanineModule.__instance = self
|
|
|
|
self._subcommand_table = {
|
|
'subscribe': self._subscribe,
|
|
'unsubscribe': self._unsubscribe,
|
|
'hilfe': self._help,
|
|
'*': self._any
|
|
}
|
|
self._channels = self._stm.get_data('channels')
|
|
if not self._channels:
|
|
# TODO: Move out of the constructor. Perform this asynchronously,
|
|
# and just refuse to process commands before we're done. Start the
|
|
# request loop afterwards.
|
|
logger.info('Channels do not exist. Downloading...')
|
|
req = requests.get(MiscDataSources.channels())
|
|
channels = req.json()
|
|
self._channels = [channels[key].get('NAME', '') for key in channels]
|
|
self._stm.set_data('channels', self._channels)
|
|
logger.info('Done')
|
|
|
|
self._warnings0 = list(map(stub_warning, self._stm.get_data('warnings')))
|
|
self._warnings1 = []
|
|
self._refresh_timeout = self.get_option('refresh_timeout', 15 * 60)
|
|
self._sources = list(map(WarningSources.source_by_name, self._config['sources']))
|
|
|
|
loop = asyncio.get_event_loop()
|
|
periodic = loop.create_task(self._periodic_ticker())
|
|
|
|
async def _periodic_ticker(self):
|
|
'''
|
|
"Executes" every self._refresh_timeout seconds to fetch all
|
|
configured warnings and send them to the users, if there are
|
|
any new ones.
|
|
'''
|
|
while True:
|
|
logger.debug('Refreshing warning list')
|
|
await self._request_warnings()
|
|
|
|
self._stm.set_data('warnings',
|
|
[x.id for x in self._warnings0])
|
|
|
|
await asyncio.sleep(self._refresh_timeout)
|
|
|
|
async def _request_warnings(self):
|
|
'''
|
|
Requests warnings from all configured warning sources and
|
|
sends new ones as notifications
|
|
'''
|
|
self._warnings1 = self._warnings0
|
|
self._warnings0 = []
|
|
for source in self._sources:
|
|
try:
|
|
req = requests.get(source)
|
|
self._warnings0 += parse_data(req.json())
|
|
except MaxRetryError:
|
|
logger.warn('Connection timeout for request to %s', source)
|
|
continue
|
|
|
|
# Find new warnings and send them out
|
|
ids = [x.id for x in self._warnings1]
|
|
for warning in self._warnings0:
|
|
if warning.id in ids:
|
|
continue
|
|
|
|
for jid, _ in self._sum.get_subscriptions_for_keywords(warning.landkreise):
|
|
body = format_warning(warning)
|
|
self.send_message(aioxmpp.JID.fromstr(jid), body)
|
|
|
|
async def _subscribe(self, cmd, msg):
|
|
if len(cmd) < 2:
|
|
self.send_message(msg.from_, 'Du hast keinen Landkreis angegeben')
|
|
return
|
|
|
|
landkreis = ' '.join(cmd[1:])
|
|
if not landkreis in self._channels:
|
|
self.send_message(msg.from_, 'Der angegebene Landkreis "%s" existiert nicht' % (landkreis))
|
|
return
|
|
|
|
bare = str(msg.from_.bare())
|
|
if self._sum.is_subscribed_to(bare, landkreis):
|
|
self.send_message(msg.from_,
|
|
'Du hast den "%s" bereits abonniert' % (landkreis))
|
|
return
|
|
|
|
self._sum.add_subscription_for(bare, landkreis)
|
|
for warning in self._warnings0:
|
|
if landkreis in warning.landkreise:
|
|
body = format_warning(warning)
|
|
self.send_message(msg.from_, body)
|
|
|
|
async def _unsubscribe(self, cmd, msg):
|
|
if len(cmd) < 2:
|
|
self.send_message(msg.from_, 'Du hast keinen Landkreis angegeben')
|
|
return
|
|
|
|
landkreis = ' '.join(cmd[1:])
|
|
bare = str(msg.from_.bare())
|
|
if not self._sum.is_subscribed_to(bare, landkreis):
|
|
self.send_message(msg.from_, 'Du hast "%s" nicht abonniert' % (landkreis))
|
|
return
|
|
|
|
self._sum.remove_subscription_for(bare, landkreis)
|
|
self.send_message(msg.from_, 'Du erhälst nun keine Nachrichten zu "%s" mehr' % (landkreis))
|
|
|
|
async def _help(self, cmd, msg):
|
|
body = '''Verfügbare Befehle:
|
|
|
|
subscribe <Landkreis> - Abonniere einen Landkreis
|
|
unsubscribe <Landkreis> - Entferne das Abonnement zu einem Landkreis
|
|
help - Gebe diese Hilfe aus'''
|
|
self.send_message(msg.from_, body)
|
|
|
|
async def _any(self, cmd, msg):
|
|
if not cmd:
|
|
self.send_message(msg.from_,
|
|
'Ich bin die Jabber Anwendung für Notfallinformations- und -Nachrichten-Empfang')
|
|
else:
|
|
self.send_message(msg.from_,
|
|
'Unbekannter Befehl "%s". "janine hilfe" gibt alle bekannten Befehle aus' % (cmd[0]))
|
|
|
|
|
|
def get_instance(base, **kwargs):
|
|
return JanineModule.get_instance(base, **kwargs)
|