initial commit

Signed-off-by: klux2 <k.lux.gm@gmail.com>
This commit is contained in:
klux2 2019-03-17 18:48:05 +01:00
commit b93b745cf0
8 changed files with 1079 additions and 0 deletions

83
.gitignore vendored Normal file
View file

@ -0,0 +1,83 @@
# Created by https://www.gitignore.io/api/pycharm+iml
# Edit at https://www.gitignore.io/?templates=pycharm+iml
### PyCharm+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### PyCharm+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# End of https://www.gitignore.io/api/pycharm+iml
*.log

4
.idea/encodings.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

127
Daemon.py Normal file
View file

@ -0,0 +1,127 @@
"""Generic linux daemon base class for python 3.x."""
import atexit
import os
import signal
import sys
import time
# noinspection SpellCheckingInspection
class Daemon:
"""A generic daemon class.
Usage: subclass the daemon class and override the run() method."""
def __init__(self, pidfile):
self.pidfile = pidfile
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir('/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(os.devnull, 'a+')
se = open(os.devnull, 'a+')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
with open(self.pidfile, 'w+') as f:
f.write(pid + '\n')
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""Start the daemon."""
# Check for a pidfile to see if the daemon already runs
try:
with open(self.pidfile, 'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if pid:
message = "pidfile {0} already exist. " + \
"Daemon already running?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""Stop the daemon."""
# Get the pid from the pidfile
try:
with open(self.pidfile, 'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if not pid:
message = "pidfile {0} does not exist. " + \
"Daemon not running?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print(str(err.args))
sys.exit(1)
def restart(self):
"""Restart the daemon."""
self.stop()
self.start()
def run(self):
"""You should override this method when you subclass Daemon.
It will be called after the process has been daemonized by
start() or restart()."""

202
HSMensaW_bot.py Normal file
View file

@ -0,0 +1,202 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import datetime
import json
import signal
import ssl
import sys
import urllib.request
import xml.etree.ElementTree as ET
from time import sleep
from urllib.error import HTTPError
import telepot
from telepot.loop import MessageLoop
class Essen:
def __init__(self, name, preis, kategorie):
self.name = name
self.preis = preis
self.kategorie = kategorie
def __str__(self):
return str("*%s*: %s (%.2f €)" % (self.kategorie, self.name, self.preis))
config_filename = "config.json"
monate = ["Januar", "Februar", "März", "April", "Mai", "Juni", "August", "September", "Oktober", "November", "Dezember"]
wochentage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
status = ""
def handle(msg):
global essen, status
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type == "text":
text = str(msg["text"]).lower()
if text.startswith("/start"):
if chat_id not in ids:
ids.append(chat_id)
bot.sendMessage(chat_id, "Bot wurde aktiviert")
chat = get_chat_name(msg)
send_status("Bot aktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
bot.sendMessage(chat_id, "Bot war bereits aktiviert")
elif text.startswith("/stop"):
if chat_id in ids:
ids.remove(chat_id)
bot.sendMessage(chat_id, "Bot wurde deaktiviert")
chat = get_chat_name(msg)
send_status("Bot deaktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
bot.sendMessage(chat_id, "Bot war nicht aktiv")
elif text.startswith("/essen") or text.startswith("/mensa") or text.startswith("/speiseplan"):
chat = get_chat_name(msg)
send_status("Essen angefordert für Chat %s (ID: %i)" % (chat, chat_id))
var = get_essen()
if len(essen) == 0:
if var:
bot.sendMessage(chat_id, "Es ist ein Fehler aufgetreten. Bitte später erneut versuchen.")
else:
bot.sendMessage(chat_id, "Für heute ist leider kein Speiseplan verfügbar.")
else:
send_essen(chat_id)
send_status("Essen versendet für Chat %s (ID: %i)" % (chat, chat_id))
elif text.startswith("/status") and chat_id in config_ids:
bot.sendMessage(chat_id, status)
elif content_type == "new_chat_member":
if msg["new_chat_participant"]["id"] == bot.getMe()["id"]:
bot.sendMessage(chat_id, "*Mensa-Bot der Hochschule Mittweida (beta)*\nDieser Bot versendet jeden Tag um "
"8 Uhr den aktuellen Mensa-Speiseplan. Er wird über /start für den aktuellen Chat "
"oder die aktuelle Gruppe gestartet, /stop beendet ihn wieder. Mit /essen, /mensa "
"und /speiseplan kann der aktuelle Speiseplan manuell abgerufen werden.\n\n"
"_Haftungsausschluss: Dieser Bot steht in keiner Verbindung mit der Hochschule "
"Mittweida oder dem Studentenwerk Freiberg. Alle Angaben ohne Gewähr._",
"markdown")
elif content_type == "left_chat_member":
if msg["left_chat_participant"]["id"] == bot.getMe()["id"]:
if chat_id in ids:
ids.remove(chat_id)
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
def send_essen(chat_id):
global datum, essen
nachricht = "Speiseplan am WOCHENTAG, den %s:\n" % datum.strftime("%d. MONAT %Y")
nachricht = nachricht.replace("MONAT", monate[(datum.month - 1) % 12])
nachricht = nachricht.replace("WOCHENTAG", wochentage[datum.weekday()])
for i in essen:
nachricht += "- " + str(i).replace(".", ",") + "\n\n"
bot.sendMessage(chat_id, nachricht, "markdown")
def send_status(text):
global config_ids
for chat_id in config_ids:
bot.sendMessage(chat_id, text)
def get_essen():
global datum, essen, ctx
essen = []
try:
# response = urllib.request.urlopen(url, context=ctx)
response = urllib.request.urlopen(url)
except HTTPError:
return False
except Exception as expt:
send_status("Es ist ein Fehler aufgetreten:\n%s" % str(expt))
print("Fehler:\n%s" % str(expt))
return True
data = response.read()
response.close()
text = data.decode('utf-8')
et = ET.fromstring(text)
date = et.findall("./menus/day/date")[0].text
datum = datetime.date(int(date[:4]), int(date[5:7]), int(date[8:10]))
menus = et.findall("./menus/day/menu")
for i in menus:
kategorie = i.findall("type")[0].text
name = i.findall("description")[0].text.strip("()1234567890, ")
preis = float(i.findall("./prices/price[label='Studenten']/value")[0].text.replace(",", "."))
essen.append(Essen(name, preis, kategorie))
def get_chat_name(msg):
if msg["chat"]["type"] == "group":
chat = msg["chat"]["title"] + " (g)"
elif msg["chat"]["type"] == "private":
chat = msg["chat"]["first_name"] + " " + msg["chat"]["last_name"] + " (p)"
else:
chat = "null"
return chat
def shutdown(signum, frame):
print('Signal handler called with signal', signum)
sys.stdout.close()
sys.stderr.close()
sys.exit(-1)
sys.stdout = open("out.log", "a")
sys.stderr = open("err.log", "a")
signal.signal(signal.SIGTERM, shutdown)
try:
with open(config_filename, 'r') as config_file:
config = json.load(config_file)
except FileNotFoundError as e:
print('Error: config file "{}" not found: {}'.format(config_filename, e))
sys.exit(-1)
except ValueError as e:
print('Error: invalid config file "{}": {}'.format(config_filename, e))
sys.exit(-1)
telegram_bot_token = config.get("telegram_bot_token")
url = config.get("url")
ids = config.get("ids")
config_ids = config.get("config_ids")
bot = telepot.Bot(telegram_bot_token)
MessageLoop(bot, handle).run_as_thread()
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
while True:
now = datetime.datetime.today()
nextDay = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1, 28800)
send_status("Wartezeit: %i Sekunden" % (nextDay - now).seconds)
status = "Warten bis %s" % nextDay
sleep((nextDay - now).seconds)
send_status("Aufwachen um 8 Uhr")
status = "Essen abrufen"
get_essen()
send_status("%i Essen gefunden" % len(essen))
status = "Essen senden"
if len(essen) > 0:
send_status("Essen werden gesendet")
for i in ids:
send_essen(i)
send_status("Abgeschlossen, warte 30 Sekunden")
status = "Warte 30 Sekunden"
sleep(30)

252
HSMensaW_botA.py Normal file
View file

@ -0,0 +1,252 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import asyncio
import datetime
import json
import signal
import sys
import urllib.request
import xml.etree.ElementTree as ET
from urllib.error import HTTPError
import telepot
from telepot.aio.delegate import per_chat_id, create_open, pave_event_space
from telepot.aio.loop import MessageLoop
class Essen:
def __init__(self, name, preis, kategorie):
self.name = name
self.preis = preis
self.kategorie = kategorie
def __str__(self):
if self.preis > 0:
return str("*%s*: %s (%.2f €)" % (self.kategorie, self.name, self.preis))
else:
return str("*%s*: %s" % (self.kategorie, self.name))
config_filename = "config.json"
monate = ["Januar", "Februar", "März", "April", "Mai", "Juni", "August", "September", "Oktober", "November", "Dezember"]
wochentage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
info_str = "*Mensa-Bot der Hochschule Mittweida (beta)*\nDieser Bot versendet jeden Tag um 10 Uhr den aktuellen " \
"Mensa-Speiseplan. Er wird über /start für den aktuellen Chat oder die aktuelle Gruppe gestartet, /stop " \
"beendet ihn wieder. Mit /essen, /mensa und /speiseplan kann der aktuelle Speiseplan manuell abgerufen " \
"werden.\n\n_Haftungsausschluss: Dieser Bot steht in keiner Verbindung mit der Hochschule Mittweida oder" \
" dem Studentenwerk Freiberg. Alle Angaben ohne Gewähr._"
status = ""
class HSMensaW(telepot.aio.helper.ChatHandler):
async def on_chat_message(self, msg):
global status, config
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type == "text":
text = str(msg["text"]).lower()
if text.startswith("/start"):
if chat_id not in ids:
ids.append(chat_id)
await bot.sendMessage(chat_id, "Bot wurde aktiviert")
chat = get_chat_name(msg)
await send_status("Bot aktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
await bot.sendMessage(chat_id, "Bot war bereits aktiviert")
elif text.startswith("/stop"):
if chat_id in ids:
ids.remove(chat_id)
await bot.sendMessage(chat_id, "Bot wurde deaktiviert")
chat = get_chat_name(msg)
await send_status("Bot deaktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
await bot.sendMessage(chat_id, "Bot war nicht aktiv")
elif text.startswith("/essen") or text.startswith("/mensa") or text.startswith("/speiseplan"):
chat = get_chat_name(msg)
await send_status("Essen angefordert für Chat %s (ID: %i)" % (chat, chat_id))
loop = asyncio.get_event_loop()
fut = asyncio.Future()
loop.run_until_complete(get_essen(fut))
(datum, essen) = fut.result()
# datum, essen = loop.run_until_complete(get_essen())
if type(essen) == bool:
if essen:
await bot.sendMessage(chat_id, "Es ist ein Fehler aufgetreten. Bitte später erneut versuchen.")
else:
await bot.sendMessage(chat_id, "Für heute ist leider kein Speiseplan verfügbar.")
else:
await send_essen(chat_id, datum, essen)
await send_status("Essen versendet für Chat %s (ID: %i)" % (chat, chat_id))
elif text.startswith("/status") and chat_id in config_ids:
await bot.sendMessage(chat_id, status)
elif text.startswith("/info"):
await bot.sendMessage(chat_id, info_str, "markdown")
elif content_type == "new_chat_member":
if msg["new_chat_participant"]["id"] == get_bot_id():
await bot.sendMessage(chat_id, info_str, "markdown")
elif content_type == "left_chat_member":
if msg["left_chat_participant"]["id"] == get_bot_id():
if chat_id in ids:
ids.remove(chat_id)
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
async def send_essen(chat_id, datum, essen):
nachricht = "Speiseplan am WOCHENTAG, den %s:\n" % datum.strftime("%d. MONAT %Y")
nachricht = nachricht.replace("MONAT", monate[(datum.month - 1) % 12])
nachricht = nachricht.replace("WOCHENTAG", wochentage[datum.weekday()])
for i in essen:
nachricht += "- " + str(i).replace(".", ",") + "\n\n"
await bot.sendMessage(chat_id, nachricht, "markdown")
async def send_status(text):
global config_ids
for chat_id in config_ids:
await bot.sendMessage(chat_id, text)
async def get_essen(future):
# global ctx
essen = []
datum = datetime.date.today()
try:
# response = urllib.request.urlopen(url, context=ctx)
response = urllib.request.urlopen(url)
except HTTPError:
future.set_result((datum, False))
return
except Exception as expt:
await send_status("Es ist ein Fehler aufgetreten:\n%s" % str(expt))
print("Fehler:\n%s" % str(expt))
future.set_result((datum, True))
return
data = response.read()
response.close()
text = data.decode('utf-8')
et = ET.fromstring(text)
date = et.findall("./menus/day/date")[0].text
datum = datetime.date(int(date[:4]), int(date[5:7]), int(date[8:10]))
menus = et.findall("./menus/day/menu")
for i in menus:
kategorie = i.findall("type")[0].text
name = i.findall("description")[0].text.strip("()1234567890, ")
try:
preis = float(i.findall("./prices/price[label='Studenten']/value")[0].text.replace(",", "."))
except ValueError:
preis = -1
essen.append(Essen(name, preis, kategorie))
future.set_result((datum, essen))
return
def get_bot_id():
api_url = "https://api.telegram.org/bot" + telegram_bot_token + "/getMe"
with urllib.request.urlopen(api_url) as response:
data = json.load(response)
bot_id = data["result"]["id"]
return bot_id
def get_chat_name(msg):
if msg["chat"]["type"] == "group":
chat = msg["chat"]["title"] + " (g)"
elif msg["chat"]["type"] == "private":
chat = msg["chat"]["first_name"] + " " + msg["chat"]["last_name"] + " (p)"
else:
chat = "null"
return chat
def shutdown(signum, frame):
print('Signal handler called with signal', signum)
ml.close()
loop.stop()
loop.close()
sys.stdout.close()
sys.stderr.close()
sys.exit(-1)
async def essen_loop():
global status
while True:
now = datetime.datetime.today()
next_day = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1, 36000)
await send_status("Wartezeit: %i Sekunden" % (next_day - now).seconds)
status = "Warten bis %s" % next_day
await asyncio.sleep((next_day - now).seconds)
await send_status("Aufwachen um 10 Uhr")
status = "Essen abrufen"
datum, essen = get_essen()
await send_status("%i Essen gefunden" % len(essen))
status = "Essen senden"
if type(essen) == list and len(essen) > 0:
await send_status("Essen werden gesendet")
for i in ids:
await send_essen(i, datum, essen)
await send_status("Abgeschlossen, warte 30 Sekunden")
status = "Warte 30 Sekunden"
await asyncio.sleep(30)
sys.stdout = open("out.log", "a")
sys.stderr = open("err.log", "a")
signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)
try:
with open(config_filename, 'r') as config_file:
config = json.load(config_file)
except FileNotFoundError as e:
print('Error: config file "{}" not found: {}'.format(config_filename, e))
sys.exit(-1)
except ValueError as e:
print('Error: invalid config file "{}": {}'.format(config_filename, e))
sys.exit(-1)
telegram_bot_token = config.get("telegram_bot_token")
url = config.get("url")
ids = config.get("ids")
config_ids = config.get("config_ids")
bot = telepot.aio.DelegatorBot(telegram_bot_token, [
pave_event_space()(
per_chat_id(), create_open, HSMensaW, timeout=10
),
])
loop = asyncio.get_event_loop()
# ml = MessageLoop(bot, handle).run_forever()
ml = MessageLoop(bot).run_forever()
loop.create_task(ml)
loop.create_task(essen_loop())
loop.run_forever()
# ctx = ssl.create_default_context()
# ctx.check_hostname = False
# ctx.verify_mode = ssl.CERT_NONE

249
HSMensaW_botA.py.BAK Normal file
View file

@ -0,0 +1,249 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import asyncio
import datetime
import json
import signal
import sys
import urllib.request
import xml.etree.ElementTree as ET
from urllib.error import HTTPError
import telepot
from telepot.aio.delegate import per_chat_id, create_open, pave_event_space
from telepot.aio.loop import MessageLoop
class Essen:
def __init__(self, name, preis, kategorie):
self.name = name
self.preis = preis
self.kategorie = kategorie
def __str__(self):
if self.preis > 0:
return str("*%s*: %s (%.2f €)" % (self.kategorie, self.name, self.preis))
else:
return str("*%s*: %s" % (self.kategorie, self.name))
config_filename = "config.json"
monate = ["Januar", "Februar", "März", "April", "Mai", "Juni", "August", "September", "Oktober", "November", "Dezember"]
wochentage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
info_str = "*Mensa-Bot der Hochschule Mittweida (beta)*\nDieser Bot versendet jeden Tag um 10 Uhr den aktuellen " \
"Mensa-Speiseplan. Er wird über /start für den aktuellen Chat oder die aktuelle Gruppe gestartet, /stop " \
"beendet ihn wieder. Mit /essen, /mensa und /speiseplan kann der aktuelle Speiseplan manuell abgerufen " \
"werden.\n\n_Haftungsausschluss: Dieser Bot steht in keiner Verbindung mit der Hochschule Mittweida oder" \
" dem Studentenwerk Freiberg. Alle Angaben ohne Gewähr._"
status = ""
essen = []
var = True
class HSMensaW(telepot.aio.helper.ChatHandler):
async def on_chat_message(self, msg):
global config, essen, status, var
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type == "text":
text = str(msg["text"]).lower()
if text.startswith("/start"):
if chat_id not in ids:
ids.append(chat_id)
config['ids'] = ids
await bot.sendMessage(chat_id, "Bot wurde aktiviert")
chat = get_chat_name(msg)
await send_status("Bot aktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
await bot.sendMessage(chat_id, "Bot war bereits aktiviert")
elif text.startswith("/stop"):
if chat_id in ids:
ids.remove(chat_id)
config['ids'] = ids
await bot.sendMessage(chat_id, "Bot wurde deaktiviert")
chat = get_chat_name(msg)
await send_status("Bot deaktiviert für Chat %s (ID: %i)" % (chat, chat_id))
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
else:
await bot.sendMessage(chat_id, "Bot war nicht aktiv")
elif text.startswith("/essen") or text.startswith("/mensa") or text.startswith("/speiseplan"):
chat = get_chat_name(msg)
await send_status("Essen angefordert für Chat %s (ID: %i)" % (chat, chat_id))
await get_essen()
if len(essen) == 0:
if var:
await bot.sendMessage(chat_id, "Es ist ein Fehler aufgetreten. Bitte später erneut versuchen.")
else:
await bot.sendMessage(chat_id, "Für heute ist leider kein Speiseplan verfügbar.")
else:
await send_essen(chat_id)
await send_status("Essen versendet für Chat %s (ID: %i)" % (chat, chat_id))
elif text.startswith("/status") and chat_id in config_ids:
await bot.sendMessage(chat_id, status)
elif text.startswith("/info"):
await bot.sendMessage(chat_id, info_str, "markdown")
elif content_type == "new_chat_member":
if msg["new_chat_participant"]["id"] == get_bot_id():
await bot.sendMessage(chat_id, info_str, "markdown")
elif content_type == "left_chat_member":
if msg["left_chat_participant"]["id"] == get_bot_id():
if chat_id in ids:
ids.remove(chat_id)
config['ids'] = ids
with open(config_filename, 'w') as outfile:
json.dump(config, outfile)
async def send_essen(chat_id):
global datum, essen
nachricht = "Speiseplan am WOCHENTAG, den %s:\n" % datum.strftime("%d. MONAT %Y")
nachricht = nachricht.replace("MONAT", monate[(datum.month - 1) % 12])
nachricht = nachricht.replace("WOCHENTAG", wochentage[datum.weekday()])
for i in essen:
nachricht += "- " + str(i).replace(".", ",") + "\n\n"
await bot.sendMessage(chat_id, nachricht, "markdown")
async def send_status(text):
global config_ids
for chat_id in config_ids:
await bot.sendMessage(chat_id, text)
async def get_essen():
global datum, essen, var # , ctx
essen = []
try:
# response = urllib.request.urlopen(url, context=ctx)
response = urllib.request.urlopen(url)
except HTTPError:
var = False
return
except Exception as expt:
await send_status("Es ist ein Fehler aufgetreten:\n%s" % str(expt))
print("Fehler:\n%s" % str(expt))
var = True
return
data = response.read()
response.close()
text = data.decode('utf-8')
et = ET.fromstring(text)
date = et.findall("./menus/day/date")[0].text
datum = datetime.date(int(date[:4]), int(date[5:7]), int(date[8:10]))
menus = et.findall("./menus/day/menu")
for i in menus:
kategorie = i.findall("type")[0].text
name = i.findall("description")[0].text.strip("()1234567890, ")
try:
preis = float(i.findall("./prices/price[label='Studenten']/value")[0].text.replace(",", "."))
except ValueError:
preis = -1
essen.append(Essen(name, preis, kategorie))
def get_bot_id():
api_url = "https://api.telegram.org/bot" + telegram_bot_token + "/getMe"
with urllib.request.urlopen(api_url) as response:
data = json.load(response)
bot_id = data["result"]["id"]
return bot_id
def get_chat_name(msg):
if msg["chat"]["type"] == "group":
chat = msg["chat"]["title"] + " (g)"
elif msg["chat"]["type"] == "private":
chat = msg["chat"]["first_name"] + " " + msg["chat"]["last_name"] + " (p)"
else:
chat = "null"
return chat
def shutdown(signum, frame):
print('Signal handler called with signal', signum)
ml.close()
loop.stop()
loop.close()
sys.stdout.close()
sys.stderr.close()
sys.exit(-1)
async def essen_loop():
global status, essen, ids
while True:
now = datetime.datetime.today()
next_day = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1, 36000)
await send_status("Wartezeit: %i Sekunden" % (next_day - now).seconds)
status = "Warten bis %s" % next_day
await asyncio.sleep((next_day - now).seconds)
await send_status("Aufwachen um 10 Uhr")
status = "Essen abrufen"
await get_essen()
await send_status("%i Essen gefunden" % len(essen))
status = "Essen senden"
if len(essen) > 0:
await send_status("Essen werden gesendet")
for i in ids:
await send_essen(i)
await send_status("Abgeschlossen, warte 30 Sekunden")
status = "Warte 30 Sekunden"
await asyncio.sleep(30)
sys.stdout = open("out.log", "a")
sys.stderr = open("err.log", "a")
signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)
try:
with open(config_filename, 'r') as config_file:
config = json.load(config_file)
except FileNotFoundError as e:
print('Error: config file "{}" not found: {}'.format(config_filename, e))
sys.exit(-1)
except ValueError as e:
print('Error: invalid config file "{}": {}'.format(config_filename, e))
sys.exit(-1)
telegram_bot_token = config.get("telegram_bot_token")
url = config.get("url")
ids = config.get("ids")
config_ids = config.get("config_ids")
bot = telepot.aio.DelegatorBot(telegram_bot_token, [
pave_event_space()(
per_chat_id(), create_open, HSMensaW, timeout=10
),
])
loop = asyncio.get_event_loop()
# ml = MessageLoop(bot, handle).run_forever()
ml = MessageLoop(bot).run_forever()
loop.create_task(ml)
loop.create_task(essen_loop())
loop.run_forever()
# ctx = ssl.create_default_context()
# ctx.check_hostname = False
# ctx.verify_mode = ssl.CERT_NONE

156
HSMensaW_daemon.py Normal file
View file

@ -0,0 +1,156 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import datetime
import json
import sys
import urllib.request
import xml.etree.ElementTree as ET
from time import sleep
from urllib.error import HTTPError
import telepot
from telepot.loop import MessageLoop
from Daemon import Daemon
class Essen:
def __init__(self, name, preis, kategorie):
self.name = name
self.preis = preis
self.kategorie = kategorie
def __str__(self):
return str("%s: *%s* (%.2f €)" % (self.kategorie, self.name, self.preis))
class HSMensaW_daemon(Daemon):
def run(self):
while True:
now = datetime.datetime.today()
nextDay = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1, 36000)
print("Sleeping: " + str((nextDay - now).seconds))
sleep((nextDay - now).seconds)
get_essen()
if len(essen) > 0:
for i in ids:
send_essen(i)
sleep(30)
config_filename = "config.json"
monate = ["Januar", "Februar", "März", "April", "Mai", "Juni", "August", "September", "Oktober", "November", "Dezember"]
wochentage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
def handle(msg):
global essen
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type == "text":
text = str(msg["text"]).lower()
if msg["text"].startswith("/start"):
ids.append(chat_id)
bot.sendMessage(chat_id, "Bot wurde aktiviert")
with open('config.json', 'w') as outfile:
json.dump(config, outfile)
elif msg["text"].startswith("/stop"):
try:
ids.remove(chat_id)
bot.sendMessage(chat_id, "Bot wurde deaktiviert")
with open('config.json', 'w') as outfile:
json.dump(config, outfile)
except ValueError:
bot.sendMessage(chat_id, "Bot war nicht aktiv")
elif msg["text"].startswith("/essen") or msg["text"].startswith("/mensa") or \
msg["text"].startswith("/speiseplan"):
get_essen()
if len(essen) == 0:
bot.sendMessage(chat_id, "Für heute ist leider kein Speiseplan verfügbar.")
else:
send_essen(chat_id)
elif content_type == "new_chat_member":
if msg["new_chat_participant"]["id"] == bot.getMe()["id"]:
bot.sendMessage(chat_id, "*Mensa-Bot der Hochschule Mittweida*\nDieser Bot versendet jeden Tag um 10 Uhr "
"den aktuellen Mensa-Speiseplan. Er wird über /start für den aktuellen Chat oder "
"die aktuelle Gruppe gestartet, /stop beendet ihn wieder. Mit /essen, /mensa und "
"/speiseplan kann der aktuelle Speiseplan manuell abgerufen werden.\n\n"
"_Haftungsausschluss: Dieser Bot steht in keiner Verbindung mit der Hochschule "
"Mittweida oder dem Studentenwerk Freiberg. Alle Angaben ohne Gewähr._",
"markdown")
elif content_type == "left_chat_member":
if msg["left_chat_participant"]["id"] == bot.getMe()["id"]:
if chat_id in ids:
ids.remove(chat_id)
with open('config.json', 'w') as outfile:
json.dump(config, outfile)
def send_essen(chat_id):
global datum, essen
nachricht = "Speiseplan am WOCHENTAG, den %s:\n" % datum.strftime("%d. MONAT %Y")
nachricht = nachricht.replace("MONAT", monate[datum.month])
nachricht = nachricht.replace("WOCHENTAG", wochentage[datum.weekday()])
for i in essen:
nachricht += str(i).replace(".", ",") + "\n"
bot.sendMessage(chat_id, nachricht, "markdown")
def get_essen():
global datum, essen
essen = []
try:
response = urllib.request.urlopen(url)
except HTTPError:
return
data = response.read()
response.close()
text = data.decode('utf-8')
et = ET.fromstring(text)
date = et.findall("./menus/day/date")[0].text
datum = datetime.date(int(date[:4]), int(date[5:7]), int(date[8:10]))
menus = et.findall("./menus/day/menu")
for i in menus:
kategorie = i.findall("type")[0].text
name = i.findall("description")[0].text.strip("()1234567890, ")
preis = float(i.findall("./prices/price[label='Studenten']/value")[0].text.replace(",", "."))
essen.append(Essen(name, preis, kategorie))
if __name__ == "__main__":
try:
with open(config_filename, 'r') as config_file:
config = json.load(config_file)
except FileNotFoundError as e:
print('Error: config file "{}" not found: {}'.format(config_filename, e))
sys.exit(-1)
except ValueError as e:
print('Error: invalid config file "{}": {}'.format(config_filename, e))
sys.exit(-1)
telegram_bot_token = config.get("telegram_bot_token")
url = config.get("url")
ids = config.get("ids")
bot = telepot.Bot(telegram_bot_token)
MessageLoop(bot, handle).run_as_thread()
daemon = HSMensaW_daemon("daemon.pid")
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
else:
print("Unknown command")
sys.exit(2)
sys.exit(0)
else:
print("usage: %s start|stop|restart" % sys.argv[0])
sys.exit(2)