diff --git a/docu/docs/modul/geocoding.md b/docu/docs/modul/geocoding.md new file mode 100644 index 0000000..ca939ea --- /dev/null +++ b/docu/docs/modul/geocoding.md @@ -0,0 +1,53 @@ +#
Geocoding
+--- + +## Beschreibung +Mit diesem Modul können einem Paket die Koordinaten eines Ortes oder einer Adresse angefügt werden. + +## Unterstützte Alarmtypen + - Pocsag + +## Resource +`geocoding` + +## Konfiguration + +|Feld|Beschreibung|Default| +|----|------------|-------| +apiProvider|Der Provider für das Geocoding| +apiToken|Der Api-Token fuer die Geocoding-Api| +geoRegex|Regex Capture-Group zum Herausfiltern der Adresse| + +#### Verfügbare Geocoding Provider + +|Name|Einstellungswert| +|----|------------| +|Mapbox|mapbox| +|Google Maps|google| + +**Beispiel:** +```yaml + - type: module + name: Geocoding Module + res: geocoding + config: + apiProvider: "{{ Provider für Geocoding }}" + apiToken: "{{ API-Key für Provider }}" + regex: "((?:[^ ]*,)*?)" +``` + +--- +## Abhängigkeiten + +- geocoder +- re + +--- +## Paket Modifikationen + +- Im Paket werden die Felder `lat` und `lng` hinterlegt + +--- +## Zusätzliche Wildcards + +- keine \ No newline at end of file diff --git a/docu/docs/plugin/telegram.md b/docu/docs/plugin/telegram.md index 1f1b3d4..d6afdfb 100644 --- a/docu/docs/plugin/telegram.md +++ b/docu/docs/plugin/telegram.md @@ -2,38 +2,20 @@ --- ## Beschreibung -Mit diesem Plugin ist es moeglich, Telegram-Nachrichten für POCSAG-Alarmierungen zu senden. -Außerdem unterstützt das Plugin das Versenden von Location über folgende geocoding-Api's: +Mit diesem Plugin ist es moeglich, Telegram-Nachrichten für POCSAG-Alarmierungen zu senden. Außerdem werden Locations versenden, wenn die Felder `lat` und `lng` im Paket definiert sind. -- Mapbox -- Google Maps +## Unterstütze Alarmtypen +- Pocsag ## Resource `telegram` ## Konfiguration -|Feld|Beschreibung|Default| -|----|------------|-------| -|name|Beliebiger Name des Plugins|| - -#### `config:` - |Feld|Beschreibung|Default| |----|------------|-------| |botToken|Der Api-Key des Telegram-Bots|| -|chatId|Die Chat-Id des Empfängers / der Emfänger-Gruppe|| -|geocoding|Aktivieren des Geocodings|false| -|geoRegex|Regex Capture-Group zum Herausfiltern der Adresse|| -|geoApiProvider|Der Provider für das Geocoding|| -|geoApiToken|Der Api-Token fuer die Geocoding-Api|| - -#### Verfügbare Geocoding Provider - -|Name|Einstellungswert| -|----|------------| -|Mapbox|mapbox| -|Google Maps|google| +|chatIds|Liste mit Chat-Ids der Empfängers / der Emfänger-Gruppen|| **Beispiel:** ```yaml @@ -41,19 +23,15 @@ Außerdem unterstützt das Plugin das Versenden von Location über folgende geoc name: Telegram Plugin res: telegram config: - botToken: {{ Telegram Bot Token }} - chatId: {{ Telegram Chat Id }} - geocoding: true - geoRegex: ((?:[^ ]*,)*?) - geoApiProvider: mapbox - geoApiToken: {{ Mapbox Api Key }} + botToken: "{{ Telegram Bot Token }}" + chatIds: + - "{{ Telegram Chat Id }}" ``` --- ## Abhängigkeiten - python-telegram-bot -- geocoder --- ## Paket Modifikationen diff --git a/docu/mkdocs.yml b/docu/mkdocs.yml index 846363e..5ba0bc9 100644 --- a/docu/mkdocs.yml +++ b/docu/mkdocs.yml @@ -21,6 +21,7 @@ nav: - Mode Filter: modul/mode_filter.md - Regex Filter: modul/regex_filter.md - Descriptor: modul/descriptor.md + - Geocoding: modul/geocoding.md - Plugins: - Telegram: plugin/telegram.md - Entwickler: diff --git a/module/geocoding.py b/module/geocoding.py new file mode 100644 index 0000000..b2bbb5c --- /dev/null +++ b/module/geocoding.py @@ -0,0 +1,72 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: geocoding.py +@date: 01.03.2019 +@author: Jan Speller +@description: Geocoding Module +""" +import logging +from module.moduleBase import ModuleBase + +# ###################### # +# Custom plugin includes # +import geocoder +import re +# ###################### # + +logging.debug("- %s loaded", __name__) + + +class BoswatchModule(ModuleBase): + """!Description of the Module""" + def __init__(self, config): + """!Do not change anything here!""" + super().__init__(__name__, config) # you can access the config class on 'self.config' + + def doWork(self, bwPacket): + """!start an run of the module. + + @param bwPacket: A BOSWatch packet instance""" + if bwPacket.get("mode") == "pocsag": + self.geocode(bwPacket) + pass + + return bwPacket + + def geocode(self, bwPacket): + """!find address in message and get latitude and longitude + + @param bwPacket: A BOSWatch packet instance""" + try: + address = re.search(self.config.get("regex"), bwPacket.get("message"))[1] + provider = self.config.get("apiProvider") + if "mapbox" == provider: + g = geocoder.mapbox(address, key=self.config.get("apiToken")) + print(address) + elif "google" == provider: + g = geocoder.google(address, key=self.config.get("apiToken")) + else: + return bwPacket + + (lat, lng) = g.latlng + bwPacket.set("lat", lat) + bwPacket.set("lng", lng) + return bwPacket + except (IndexError, TypeError, ValueError): + logging.warning("Address was not found in current Message, skipping geocoding") + except Exception as e: + logging.error("Unknown Error while executing geocoding module: " + str(type(e).__name__) + ": " + str(e)) + return bwPacket + + def onUnload(self): + """!Called by destruction of the plugin""" + pass diff --git a/plugin/telegram.py b/plugin/telegram.py index e97c5bc..f32a0cf 100644 --- a/plugin/telegram.py +++ b/plugin/telegram.py @@ -9,22 +9,19 @@ German BOS Information Script by Bastian Schroll -@file: template_module.py -@date: 14.01.2018 -@author: Bastian Schroll -@description: Template Plugin File +@file: telegram.py +@date: 20.02.2020 +@author: Jan Speller +@description: Telegram Plugin """ import logging -from telegram.error import (TelegramError, Unauthorized, BadRequest, TimedOut, ChatMigrated, NetworkError) from plugin.pluginBase import PluginBase -import telegram -import re -import geocoder # ###################### # # Custom plugin includes # - +from telegram.error import (TelegramError, Unauthorized, BadRequest, TimedOut, ChatMigrated, NetworkError) +import telegram # ###################### # logging.debug("- %s loaded", __name__) @@ -39,75 +36,31 @@ class BoswatchPlugin(PluginBase): def onLoad(self): """!Called by import of the plugin""" - pass - - def setup(self): - """!Called before alarm""" self.bot = telegram.Bot(token=self.config.get("botToken", default="")) pass - def fms(self, bwPacket): - """!Called on FMS alarm - - @param bwPacket: bwPacket instance""" - logging.warning("Telegram Plugin does not work for FMS") - pass - def pocsag(self, bwPacket): """!Called on POCSAG alarm @param bwPacket: bwPacket instance""" + msg = bwPacket.get("ric") + " (" + bwPacket.get("subric") + ")\n" + bwPacket.get("message") - try: - # Send Message via Telegram - msg = bwPacket.get("ric") + " (" + bwPacket.get("subric") + ")\n" + bwPacket.get("message") - self.bot.send_message(chat_id=self.config.get("chatId", default=""), text=msg) - - # Send Location via Telegram if Geocoding is enabled and Provider and Key are set - if self.config.get("geocoding", default=False): - address = re.search(self.config.get("geoRegex"), bwPacket.get("message"))[1] - provider = self.config.get("geoApiProvider") - - if "mapbox" == provider: - g = geocoder.mapbox(address, key=self.config.get("geoApiToken")) - elif "google" == provider: - g = geocoder.google(address, key=self.config.get("geoApiToken")) - else: - return - - (lat, lng) = g.latlng - self.bot.sendLocation(chat_id=self.config.get("chatId", default=""), latitude=lat, longitude=lng) - except (IndexError, TypeError, ValueError): - logging.warning("Address was not found in current Message, skipping Location") - except Unauthorized: - logging.error("Error while sending Telegram Message, please Check your api-key") - except (TimedOut, NetworkError): - logging.error("Error while sending Telegram Message, please Check your connectivity") - except (BadRequest, TelegramError): - logging.error("Error while sending Telegram Message") - except Exception as e: - logging.error("Unknown Error while sending Telegram Message: " + str(type(e).__name__) + ": " + str(e)) + if bwPacket.get("lat") is not None and bwPacket.get("lng") is not None: + (lat, lng) = (bwPacket.get("lat"), bwPacket.get("lng")) + for chatId in self.config.get("chatIds", default=[]): + try: + # Send Message via Telegram + self.bot.send_message(chat_id=chatId, default=""), text=msg) - pass - - def zvei(self, bwPacket): - """!Called on ZVEI alarm - - @param bwPacket: bwPacket instance""" - logging.warning("Telegram Plugin does not work for ZVEI") - pass - - def msg(self, bwPacket): - """!Called on MSG packet - - @param bwPacket: bwPacket instance""" - logging.warning("Telegram Plugin does not work for MSG") - pass - - def teardown(self): - """!Called after alarm""" - pass - - def onUnload(self): - """!Called by destruction of the plugin""" + # Send Location via Telegram if lat and lng exist in Package + if lat is not None and lng is not None: + self.bot.sendLocation(chat_id=chatId, latitude=lat, longitude=lng) + except Unauthorized: + logging.error("Error while sending Telegram Message, please Check your api-key") + except (TimedOut, NetworkError): + logging.error("Error while sending Telegram Message, please Check your connectivity") + except (BadRequest, TelegramError): + logging.error("Error while sending Telegram Message") + except Exception as e: + logging.error("Unknown Error while sending Telegram Message: " + str(type(e).__name__) + ": " + str(e)) pass diff --git a/testPackage.py b/testPackage.py new file mode 100644 index 0000000..a9bfeb7 --- /dev/null +++ b/testPackage.py @@ -0,0 +1,134 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import logging +import socket +import select + +logging.debug("- %s loaded", __name__) + +HEADERSIZE = 10 +class TCPClient: + """!TCP client class""" + + def __init__(self, timeout=3): + """!Create a new instance + + @param timeout: timeout for the client in sec. (3)""" + socket.setdefaulttimeout(timeout) + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def connect(self, host="localhost", port=8080): + """!Connect to the server + + @param host: Server IP address ("localhost") + @param port: Server Port (8080) + @return True or False""" + try: + if not self.isConnected: + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._sock.connect((host, port)) + logging.debug("connected to %s:%s", host, port) + return True + logging.warning("client always connected") + return True + except socket.error as e: + logging.error(e) + return False + + def disconnect(self): + """!Disconnect from the server + + @return True or False""" + try: + if self.isConnected: + self._sock.shutdown(socket.SHUT_RDWR) + self._sock.close() + logging.debug("disconnected") + return True + logging.warning("client always disconnected") + return True + except socket.error as e: + logging.error(e) + return False + + def transmit(self, data): + """!Send a data packet to the server + + @param data: data to send to the server + @return True or False""" + try: + logging.debug("transmitting:\n%s", data) + data = data.encode("utf-8") + header = str(len(data)).ljust(HEADERSIZE).encode("utf-8") + self._sock.sendall(header + data) + logging.debug("transmitted...") + return True + except socket.error as e: + logging.error(e) + return False + + def receive(self, timeout=1): + """!Receive data from the server + + @param timeout: to wait for incoming data in seconds + @return received data""" + try: + read, _, _ = select.select([self._sock], [], [], timeout) + if not read: # check if there is something to read + return False + + header = self._sock.recv(HEADERSIZE).decode("utf-8") + if not len(header): # check if there data + return False + + length = int(header.strip()) + received = self._sock.recv(length).decode("utf-8") + + logging.debug("recv header: '%s'", header) + logging.debug("received %d bytes: %s", len(received), received) + return received + except socket.error as e: + logging.error(e) + return False + + @property + def isConnected(self): + """!Property of client connected state""" + try: + if self._sock: + _, write, _ = select.select([], [self._sock], [], 0.1) + if write: + data = "".encode("utf-8") + header = str(len(data)).ljust(HEADERSIZE).encode("utf-8") + self._sock.sendall(header + data) + return True + return False + except socket.error as e: + if e.errno != 32: + logging.exception(e) + return False + except ValueError: + return False + +client = TCPClient() +client.connect() +client.transmit(str({ + 'serverName': 'TestServer', + 'serverVersion': '3.0', + 'serverBuildDate': '01.01.2020', + 'serverBranch': 'develop', + 'clientName': 'TestClient', + 'clientIP': '127.0.0.1', + 'clientVersion': '3.0', + 'clientBuildDate': '01.01.2020', + 'clientBranch': 'develop', + 'inputSource': 'sdr', + 'timestamp': '15:30', + 'frequency': '173.240', + 'mode': 'pocsag', + 'bitrate': '512', + 'ric': '1326089', + 'subric': '1', + 'subricText': 'a', + 'message': '17:22 FW 18865 Gebäudebrand_kle Sandkampstr.,15,-,Hopsten,NRW rauchentwicklung' +})) \ No newline at end of file