''' 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 . ''' 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 - Abonniere einen Landkreis unsubscribe - 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)