Eerste pleging!
This commit is contained in:
commit
8c6eb02a9b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.pyc
|
||||||
|
__pycache__
|
0
__init__.py
Normal file
0
__init__.py
Normal file
4
__main__.py
Normal file
4
__main__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import asyncio
|
||||||
|
from . import bot
|
||||||
|
|
||||||
|
asyncio.run(bot.main())
|
100
bot.py
Executable file
100
bot.py
Executable file
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from . import config, taalgebruik, wekober
|
||||||
|
|
||||||
|
# Telegram
|
||||||
|
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters,\
|
||||||
|
CallbackQueryHandler, CallbackContext
|
||||||
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ChatAction, ParseMode
|
||||||
|
|
||||||
|
STICKER_AL_AAN = None
|
||||||
|
API_KEY = os.environ["TG_APIKEY"];
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TELEGRAM HANDLERS #
|
||||||
|
###############################################################################
|
||||||
|
def start(update, context):
|
||||||
|
context.bot.send_message(chat_id=update.message.chat_id, text="Laden versie v2.1…")
|
||||||
|
time.sleep(5)
|
||||||
|
context.bot.send_message(chat_id=update.message.chat_id, text="Versie v2.1 geladen!")
|
||||||
|
context.bot.send_message(chat_id=update.message.chat_id, text="Ik ben terug van weggeweest en ik ben klaar om te rollen!\n\
|
||||||
|
\nJullie hebben misschien een lange tijd vrijspel gehad, maar nu sla ik terug!")
|
||||||
|
|
||||||
|
def sokpop(update, context):
|
||||||
|
if update.message.from_user.id not in config.SOKPOP_GEBRUIKERS:
|
||||||
|
update.message.reply_text("U heeft onvoldoende rechten.")
|
||||||
|
elif len(context.args) < 2:
|
||||||
|
update.message.reply_text("Gebruik als volgt: /sokpop <gespreksid> <tekst>")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
chat_id = int(context.args[0])
|
||||||
|
text = " ".join(context.args[1:])
|
||||||
|
context.bot.send_message(chat_id=chat_id, text=text)
|
||||||
|
except ValueError:
|
||||||
|
update.message.reply_text("Het eerste argument <gespreksid> moet een\
|
||||||
|
geheel getal zijn!")
|
||||||
|
|
||||||
|
async def bot_main(queue: asyncio.Queue):
|
||||||
|
global STICKER_AL_AAN
|
||||||
|
logging.info("Connecting to Telegram")
|
||||||
|
updater = Updater(token=API_KEY, use_context=True)
|
||||||
|
dispatcher = updater.dispatcher
|
||||||
|
|
||||||
|
start_handler = CommandHandler("start", start)
|
||||||
|
# Handlers from taalgebruik
|
||||||
|
dump_dataset_handler = CommandHandler("dump", taalgebruik.dump_dataset)
|
||||||
|
rapporteer_handler = CommandHandler("rapporteer", taalgebruik.rapporteer)
|
||||||
|
message_handler = MessageHandler(Filters.text, taalgebruik.handle_message)
|
||||||
|
callback_handler = CallbackQueryHandler(taalgebruik.handle_correction)
|
||||||
|
|
||||||
|
# Handlers for wekober
|
||||||
|
wekober_handler = CommandHandler("wekober", wekober.handle_wekober)
|
||||||
|
|
||||||
|
dispatcher.add_handler(start_handler)
|
||||||
|
dispatcher.add_handler(dump_dataset_handler)
|
||||||
|
dispatcher.add_handler(rapporteer_handler)
|
||||||
|
dispatcher.add_handler(callback_handler)
|
||||||
|
dispatcher.add_handler(wekober_handler)
|
||||||
|
dispatcher.add_handler(CommandHandler("sokpop", sokpop))
|
||||||
|
dispatcher.add_handler(message_handler)
|
||||||
|
|
||||||
|
STICKER_AL_AAN = updater.bot.get_sticker_set("mijnvervaardigingsoberrobot")\
|
||||||
|
.stickers[0]
|
||||||
|
|
||||||
|
|
||||||
|
updater.start_polling()
|
||||||
|
logging.info("Started bot")
|
||||||
|
#updater.idle()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
event = await queue.get()
|
||||||
|
if event["type"] == "pc_status_changed":
|
||||||
|
await wekober.on_pc_status_changed(updater.bot, event["status"])
|
||||||
|
queue.task_done()
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
logging.info("Stopping bot")
|
||||||
|
updater.stop()
|
||||||
|
|
||||||
|
# Main
|
||||||
|
async def main():
|
||||||
|
# Set up logging
|
||||||
|
queue = asyncio.Queue()
|
||||||
|
logging.basicConfig(format='[%(asctime)s %(levelname)s] %(name)s: %(message)s', level=logging.INFO)
|
||||||
|
|
||||||
|
# Initalize taalgebruik
|
||||||
|
taalgebruik.init()
|
||||||
|
t_dbus_main = asyncio.create_task(wekober.start_dbus(queue))
|
||||||
|
t_http_main = asyncio.create_task(wekober.start_http(queue))
|
||||||
|
t_bot_main = asyncio.create_task(bot_main(queue))
|
||||||
|
|
||||||
|
await asyncio.gather(t_dbus_main, t_bot_main, t_http_main)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
2
config.py
Normal file
2
config.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
BEHEERDERS = [9811869]
|
||||||
|
SOKPOP_GEBRUIKERS = [9811869]
|
138
taalgebruik.py
Normal file
138
taalgebruik.py
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
# Telegram
|
||||||
|
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler
|
||||||
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ChatAction, ParseMode
|
||||||
|
# Machine learning
|
||||||
|
from pandas import DataFrame
|
||||||
|
import numpy
|
||||||
|
from sklearn import preprocessing
|
||||||
|
from sklearn.feature_extraction.text import CountVectorizer
|
||||||
|
from sklearn.naive_bayes import MultinomialNB
|
||||||
|
from . import config
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
count_vectorizer = CountVectorizer()
|
||||||
|
classifier = MultinomialNB()
|
||||||
|
|
||||||
|
WAARHEID = 'w'
|
||||||
|
LEUGEN = 'l'
|
||||||
|
SOURCES = [
|
||||||
|
("slecht.tkst", LEUGEN),
|
||||||
|
("goed.tkst", WAARHEID),
|
||||||
|
("slecht-gebruiker.tkst", LEUGEN),
|
||||||
|
("goed-gebruiker.tkst", WAARHEID)
|
||||||
|
]
|
||||||
|
LEUGEN_ANTWOORDEN = ["Zeg makker...",
|
||||||
|
"FOUTMELDING_BERICHT_BEVAT_LEUGEN",
|
||||||
|
"Ik hoopte dat je inmiddels wijzer was.",
|
||||||
|
"Ik ben niet boos, ik ben slechts teleurgesteld",
|
||||||
|
"Zeg makkeroni...",
|
||||||
|
"Voor jou heb ik nog een leuke anagram: je bent een flikkerende flakker.",
|
||||||
|
"Zge mkaker",
|
||||||
|
"Tijd voor een handhaving",
|
||||||
|
"Jullie fantasieën kunnen ook nooit uitgedoofd worden. Jullie frikkende frikken!"]
|
||||||
|
LEUGEN_ANTWOORDEN_GEWICHT = [
|
||||||
|
25,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1]
|
||||||
|
|
||||||
|
data_frame = DataFrame({"tekst": [], "klasse": []})
|
||||||
|
|
||||||
|
def load_file(file_name):
|
||||||
|
f = open(file_name)
|
||||||
|
for line in f:
|
||||||
|
yield line
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def build_frame(file_name, classification):
|
||||||
|
rows = []
|
||||||
|
index = []
|
||||||
|
i = 0;
|
||||||
|
for message in load_file(file_name):
|
||||||
|
rows.append({"tekst": message, "klasse": classification})
|
||||||
|
index.append(file_name + ":" + str(i))
|
||||||
|
i += 1
|
||||||
|
data_frame = DataFrame(rows, index=index)
|
||||||
|
return data_frame
|
||||||
|
|
||||||
|
def retrain_data():
|
||||||
|
global data_frame
|
||||||
|
data_frame = DataFrame({"tekst": [], "klasse": []})
|
||||||
|
LOGGER.info("Loading dataset")
|
||||||
|
for file_name, classification in SOURCES:
|
||||||
|
data_frame = data_frame.append(build_frame(file_name, classification))
|
||||||
|
|
||||||
|
LOGGER.info("Transforming dataset")
|
||||||
|
counts = count_vectorizer.fit_transform(data_frame['tekst'].values)
|
||||||
|
|
||||||
|
LOGGER.info("Training model")
|
||||||
|
targets = data_frame['klasse'].values
|
||||||
|
classifier.fit(counts, targets)
|
||||||
|
|
||||||
|
def gen_leugen_message(message, isEdit):
|
||||||
|
return random.choices(LEUGEN_ANTWOORDEN, weights=LEUGEN_ANTWOORDEN_GEWICHT)[0]
|
||||||
|
|
||||||
|
def dump_dataset(update, context):
|
||||||
|
context.bot.send_message(chat_id=update.message.chat_id, text="```" + str(data_frame) + "```", parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
def handle_message(update, context):
|
||||||
|
mesg = update.effective_message
|
||||||
|
is_edit = mesg.edit_date is not None
|
||||||
|
|
||||||
|
message_counts = count_vectorizer.transform([mesg.text])
|
||||||
|
result = classifier.predict(message_counts)[0]
|
||||||
|
if result == WAARHEID:
|
||||||
|
LOGGER.info("'{}' bevat de waarheid".format(mesg.text))
|
||||||
|
elif result == LEUGEN:
|
||||||
|
context.bot.send_chat_action(chat_id=mesg.chat_id, action=ChatAction.TYPING)
|
||||||
|
keyboard = [[InlineKeyboardButton("Goed", callback_data=LEUGEN), InlineKeyboardButton("Fout", callback_data=WAARHEID)]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
mesg.reply_text(gen_leugen_message(mesg.text, is_edit), quote=True, reply_markup=reply_markup)
|
||||||
|
|
||||||
|
def handle_correction(update, context):
|
||||||
|
callback = update.callback_query
|
||||||
|
LOGGER.info("{} drukte op {}".format(callback.from_user.id, callback.data))
|
||||||
|
if callback.from_user.id in config.BEHEERDERS:
|
||||||
|
if callback.data == WAARHEID:
|
||||||
|
with open("goed-gebruiker.tkst", "a") as my_file:
|
||||||
|
my_file.write(callback.message.reply_to_message.text + "\n")
|
||||||
|
callback.answer(text="Bedankt voor de verbetering, beheerder!")
|
||||||
|
#callback.edit_message_text("Bewerking: Sorry, ik zat fout. Oeps.".format(gen_leugen_message()), parse_mode=ParseMode.MARKDOWN)
|
||||||
|
callback.message.delete()
|
||||||
|
elif callback.data == LEUGEN:
|
||||||
|
callback.answer(text="Bedankt voor de bevestiging, beheerder!")
|
||||||
|
context.bot.edit_message_reply_markup(chat_id=callback.message.chat_id, message_id=callback.message.message_id, reply_markup=InlineKeyboardMarkup([[]]))
|
||||||
|
#update.callback_query.edit_message_reply_markup(InlineKeyboardMarkup([[]]))
|
||||||
|
retrain_data()
|
||||||
|
|
||||||
|
else:
|
||||||
|
if callback.data == WAARHEID:
|
||||||
|
update.callback_query.answer(text="Bedankt voor de verbetering!")
|
||||||
|
else:
|
||||||
|
update.callback_query.answer(text="Bedankt voor de bevestiging!")
|
||||||
|
|
||||||
|
def rapporteer(update, context):
|
||||||
|
if update.message.from_user.id not in config.BEHEERDERS:
|
||||||
|
update.message.reply_text("Sorry, jij bent geen beheerder. Deze actie wordt niet uitgevoerd.")
|
||||||
|
else:
|
||||||
|
if update.message.reply_to_message is not None:
|
||||||
|
text = update.message.reply_to_message.text
|
||||||
|
else:
|
||||||
|
text = " ".join(args)
|
||||||
|
with open("slecht-gebruiker.tkst", "a") as my_file:
|
||||||
|
my_file.write(text + "\n")
|
||||||
|
update.message.reply_text("'{}' is toegevoegd aan de lijst met verboden uitdrukkingen!".format(text))
|
||||||
|
retrain_data()
|
||||||
|
|
||||||
|
def init():
|
||||||
|
"""Initializes the machine learning data"""
|
||||||
|
retrain_data()
|
||||||
|
|
324
wekober.py
Normal file
324
wekober.py
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
from aiohttp import web
|
||||||
|
from aiohttp_sse import sse_response
|
||||||
|
from enum import Enum
|
||||||
|
from jwt import JWT, jwk_from_pem
|
||||||
|
from multidict import MultiDict
|
||||||
|
from telegram import ChatAction
|
||||||
|
|
||||||
|
from . import bot as my_bot
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import aio_msgpack_rpc
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import ssl
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
import wakeonlan
|
||||||
|
|
||||||
|
class PcStatus(Enum):
|
||||||
|
ONBEKEND = 0
|
||||||
|
OPSTARTEN = 1
|
||||||
|
AAN = 2
|
||||||
|
NAAR_SLAAPSTAND = 3
|
||||||
|
SLAAPSTAND = 4
|
||||||
|
WAKKER_WORDEN = 5
|
||||||
|
UIT = 6
|
||||||
|
|
||||||
|
def can_wake(self) -> bool:
|
||||||
|
return self.value == 0 or self.value == 4 or self.value == 6
|
||||||
|
|
||||||
|
|
||||||
|
def friendly_name(self) -> str:
|
||||||
|
if self.value == 0:
|
||||||
|
return "onbekend (waarschijnlijk uit)"
|
||||||
|
elif self.value == 1:
|
||||||
|
return "opstarten"
|
||||||
|
elif self.value == 2:
|
||||||
|
return "aan"
|
||||||
|
elif self.value == 3:
|
||||||
|
return "proberen in slaap te komen"
|
||||||
|
elif self.value == 4:
|
||||||
|
return "slapen"
|
||||||
|
elif self.value == 5:
|
||||||
|
return "proberen wakker te worden"
|
||||||
|
elif self.value == 6:
|
||||||
|
return "uit"
|
||||||
|
else:
|
||||||
|
LOGGER.warning(f"Unknown status: {self.value}")
|
||||||
|
return "onbekend"
|
||||||
|
|
||||||
|
PC_STATUS = PcStatus.ONBEKEND
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
MAC = "70:85:c2:af:3c:48"
|
||||||
|
WAKE_MSGS = [("Ik zal een poging wagen om de ober wakker te maken.",
|
||||||
|
"Volgens mij is het gelukt!",
|
||||||
|
"Ik heb het geprobeerd, maar het lukte volgens mij niet",
|
||||||
|
"De ober was al wakker."),
|
||||||
|
("Een ogenblikje…", "Gelukt!", "Oh nee, het lukte niet.", "De ober staat\
|
||||||
|
al aan"),
|
||||||
|
("Nou, dat is wel moeilijk. Maar speciaal voor jou wil ik wel een\
|
||||||
|
uitzondering maken", "En speciaal voor jou staat de ober nu aan!",
|
||||||
|
"Ik heb mijn best gedaan, maar het wilde niet lukken.", "De ober was al aan\
|
||||||
|
, speciaal voor jou!"),
|
||||||
|
("Oké, ik stuur mijn boodschapper wel op een trojka naar de ober\
|
||||||
|
toe.", "Zover ik heb vernomen is mijn boodschapper aangekomen.",
|
||||||
|
"De boodschapper is helaas opgegeten door een aantal wolven.",
|
||||||
|
"De boodschapper kwam terug met de boodschap dat de ober al aan stond"),
|
||||||
|
("Hocus pocus, pilatus pas, ik wou dat de ober aan was!",
|
||||||
|
"Nou, daar is 'ie dan!", "Zit eens niet zo dicht op mijn nues. Nu is mijn\ groote goocheltruc mislukt!", "Nou, daar is 'ie dan. Snel hë?"),
|
||||||
|
("Zeg makker…", "De ober is geen specerij, maar hij staat wel aan!",
|
||||||
|
"Mijn poging om de ober in te schakelen is mislukt door de Hispanjolen."),
|
||||||
|
("Binnenkort leven jullie in een samenleving…", "…waar de ober aan staat.",
|
||||||
|
"…waar de ober niet gestart kon worden.", "…waar de ober al aan staat."),
|
||||||
|
("Oké, maar eerst moet ik nog een aantal kokosnoten aan de vulkaan opofferen.\
|
||||||
|
Zou je nog heel even geduld kunnen hebben?", "De kokosnoten zijn vernietigd\
|
||||||
|
\nde ober staat aan.\nIk ben niet creatief\nen eindig deze rijm met banaan",
|
||||||
|
"De vulkaangoden hebben mij verboden de ober aan te zetten. Helaas.",
|
||||||
|
"De ober staat al aan"),
|
||||||
|
("Ik zal wel even bij de ober langsgaan", "Hallo meneer de Ober. Ja, ik moest\
|
||||||
|
kloppen want de bel deed het niet! Maar wat fijn dat u er bent!", "De ober\
|
||||||
|
gooide de deur recht in mijn gezicht weer dicht.", "De deur was al open,\
|
||||||
|
ik kon zo naar binnen!")]
|
||||||
|
PC_IP = "roku"
|
||||||
|
PING_TIMEOUT = "2"
|
||||||
|
WWW_REALM = "Netsoj"
|
||||||
|
|
||||||
|
WAKE_REQUESTED = []
|
||||||
|
WAKE_REQUESTED_MSG = []
|
||||||
|
ALLOWED_CHATS = [-1001304423616]
|
||||||
|
ALLOWED_USERS = [9811869]
|
||||||
|
|
||||||
|
SSE_STREAMS = []
|
||||||
|
|
||||||
|
PUB_KEY = jwk_from_pem(b"""-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkFMN9Bl7SQgizcdC44aH
|
||||||
|
nM/8bgkwhIyC4WtoRpN1lD9glVe1+f2f6UkWP+49rsyGJy2fOwUcH6mOJu+R12Tr
|
||||||
|
IGChLITDogQPRfsoINttprXXSl8Koa5iA4F2EWv3IJdFlXXhIvZoEPXNrAmgaEt/
|
||||||
|
CMYUf7UXjBONzqvDFPgbW06v0wtKS+K8WC7dQZ8pSmTNOqpZe4z0hO7MfqWZ1lF9
|
||||||
|
/aH5ARlGBI2aYtVw++lxbXg5c/BpLOaD7TvmntIhumxtiEHBDJGt+QQEDVeqPKvO
|
||||||
|
zSBqZLctHAa0oLMjp1qU1cs1yHJg7VKwp2TKrfzHPfHeEVOp3/GTL50tVf+zDVNW
|
||||||
|
UQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----""");
|
||||||
|
|
||||||
|
MJWT = JWT()
|
||||||
|
|
||||||
|
def is_ober_aan() -> bool:
|
||||||
|
return os.system(f"ping -c 1 {PC_IP}") == 0
|
||||||
|
|
||||||
|
async def async_is_ober_aan() -> bool:
|
||||||
|
proc = await asyncio.create_subprocess_exec("ping", "-c", "1", "-W", PING_TIMEOUT, PC_IP)
|
||||||
|
return await proc.wait() == 0
|
||||||
|
|
||||||
|
def handle_wekober(update, context):
|
||||||
|
global WAKE_REQUESTED
|
||||||
|
global WAKE_REQUESTED_MSG
|
||||||
|
messages = random.choice(WAKE_MSGS)
|
||||||
|
|
||||||
|
context.bot.send_chat_action(update.effective_chat.id, ChatAction.TYPING)
|
||||||
|
if is_ober_aan():
|
||||||
|
context.bot.send_sticker(update.effective_chat.id, my_bot.STICKER_AL_AAN)
|
||||||
|
else:
|
||||||
|
context.bot.send_message(update.effective_chat.id, messages[0])
|
||||||
|
|
||||||
|
if update.effective_chat.id not in ALLOWED_CHATS and\
|
||||||
|
update.effective_user.id not in ALLOWED_USERS:
|
||||||
|
time.sleep(1)
|
||||||
|
context.bot.send_message(update.effective_chat.id, messages[2])
|
||||||
|
LOGGER.info(f"Unauthorised user: {update.effective_user.id}")
|
||||||
|
return
|
||||||
|
|
||||||
|
#LOGGER.info(f"Chat id: {update.effective_chat.id}")
|
||||||
|
|
||||||
|
wakeonlan.send_magic_packet(MAC)
|
||||||
|
if update.effective_chat.id not in WAKE_REQUESTED:
|
||||||
|
WAKE_REQUESTED += [update.effective_chat.id]
|
||||||
|
WAKE_REQUESTED_MSG += [messages]
|
||||||
|
|
||||||
|
async def on_pc_status_changed(bot, status):
|
||||||
|
global SSE_STREAMS
|
||||||
|
global WAKE_REQUESTED
|
||||||
|
global WAKE_REQUESTED_MSG
|
||||||
|
global PC_STATUS
|
||||||
|
PC_STATUS = status
|
||||||
|
|
||||||
|
LOGGER.info(f"New status: {status.friendly_name()}")
|
||||||
|
if status == PcStatus.AAN:
|
||||||
|
for i, chat in enumerate(WAKE_REQUESTED):
|
||||||
|
LOGGER.debug("i: {i}, chat: {chat}, msg: {WAKE_REQUESTED_MSG[i]}")
|
||||||
|
bot.send_message(chat, WAKE_REQUESTED_MSG[i][1])
|
||||||
|
|
||||||
|
WAKE_REQUESTED = []
|
||||||
|
WAKE_REQUESTED_MSG = []
|
||||||
|
|
||||||
|
# Notify the SSE-streams
|
||||||
|
t = []
|
||||||
|
for stream in SSE_STREAMS:
|
||||||
|
t.append(stream.send(json.dumps({"status": status.friendly_name(), "can_wake": status.can_wake()})))
|
||||||
|
|
||||||
|
LOGGER.info(f"Notifying clients ({len(SSE_STREAMS)})")
|
||||||
|
await asyncio.gather(*t)
|
||||||
|
LOGGER.info("Clients notified")
|
||||||
|
|
||||||
|
class PcBridgeBoot():
|
||||||
|
def __init__(self, queue):
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
|
def GetBootReason(self) -> str:
|
||||||
|
LOGGER.info("PC booted up!")
|
||||||
|
try:
|
||||||
|
self.queue.put_nowait({"type": "pc_status_changed", "status": PcStatus.AAN})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return str("Minecraft")
|
||||||
|
|
||||||
|
def NotifySleep(self) -> None:
|
||||||
|
LOGGER.info("PC going to sleep")
|
||||||
|
try:
|
||||||
|
self.queue.put_nowait({"type": "pc_status_changed", "status": PcStatus.SLAAPSTAND})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def NotifyWakeup(self) -> None:
|
||||||
|
LOGGER.info("PC woke up")
|
||||||
|
try:
|
||||||
|
self.queue.put_nowait({"type": "pc_status_changed", "status": PcStatus.AAN})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def NotifyShutdown(self) -> None:
|
||||||
|
LOGGER.info("PC shutting down")
|
||||||
|
try:
|
||||||
|
self.queue.put_nowait({"type": "pc_status_changed", "status": PcStatus.UIT})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@web.middleware
|
||||||
|
async def middleware_auth(request, handler):
|
||||||
|
""" Check if the user is authorized. """
|
||||||
|
if "Authorization" in request.headers:
|
||||||
|
parts = request.headers["Authorization"].split(" ")
|
||||||
|
|
||||||
|
if len(parts) != 2 and parts[0].casefold() != "bearer":
|
||||||
|
LOGGER.info("Invalid authorization header")
|
||||||
|
return web.Response(status=401,
|
||||||
|
headers=MultiDict({"WWW-Authenticate": f"BEARER Realm=\"{WWW_REALM}\""}))
|
||||||
|
|
||||||
|
userpass = parts[1]
|
||||||
|
elif "token" in request.query:
|
||||||
|
userpass = request.query["token"];
|
||||||
|
else:
|
||||||
|
# Authentication not in request headers.
|
||||||
|
LOGGER.info("Authorization not in request headers")
|
||||||
|
return web.Response(status=401,
|
||||||
|
headers=MultiDict({"WWW-Authenticate": f"BEARER Realm=\"{WWW_REALM}\""}))
|
||||||
|
|
||||||
|
# Continue as usual
|
||||||
|
try:
|
||||||
|
token = MJWT.decode(userpass, PUB_KEY, do_time_check=False)
|
||||||
|
except Exception as e:
|
||||||
|
LOGGER.info(f"Incorrect token: {e}")
|
||||||
|
return web.Response(status=401,
|
||||||
|
headers=MultiDict({"WWW-Authenticate": f"BEARER Realm=\"{WWW_REALM}\""}))
|
||||||
|
try:
|
||||||
|
can_turn_on = "minecraft-player" in token["realm_access"]["roles"]
|
||||||
|
except KeyError:
|
||||||
|
can_turn_on = False
|
||||||
|
|
||||||
|
if not can_turn_on:
|
||||||
|
return web.Response(status=403)
|
||||||
|
|
||||||
|
# Proceed normally:
|
||||||
|
response = await handler(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def post_boot(request):
|
||||||
|
global PC_STATUS
|
||||||
|
print(request)
|
||||||
|
print(request.headers)
|
||||||
|
# Notify the SSE streams
|
||||||
|
# t = []
|
||||||
|
if PC_STATUS == PcStatus.SLAAPSTAND:
|
||||||
|
status = PcStatus.WAKKER_WORDEN
|
||||||
|
else:
|
||||||
|
status = PcStatus.OPSTARTEN
|
||||||
|
|
||||||
|
await QUEUE.put({"type": "pc_status_changed", "status": status})
|
||||||
|
# for stream in SSE_STREAMS:
|
||||||
|
# t.append(stream.send(json.dumps({"status": "opstarten"})))
|
||||||
|
|
||||||
|
# await asyncio.gather(*t)
|
||||||
|
# Send a magic packet
|
||||||
|
wakeonlan.send_magic_packet(MAC)
|
||||||
|
return web.Response(status=205)
|
||||||
|
|
||||||
|
async def get_boot(request):
|
||||||
|
global PC_STATUS
|
||||||
|
|
||||||
|
if (await async_is_ober_aan()):
|
||||||
|
PC_STATUS = PcStatus.AAN
|
||||||
|
elif PC_STATUS == PcStatus.AAN:
|
||||||
|
PC_STATUS = PcStatus.ONBEKEND
|
||||||
|
|
||||||
|
antwoord = {"status": PC_STATUS.friendly_name(), "can_wake": PC_STATUS.can_wake()}
|
||||||
|
LOGGER.info(PC_STATUS.friendly_name())
|
||||||
|
return web.json_response(antwoord, status=200)
|
||||||
|
|
||||||
|
async def get_boot_stand_van_zaken(request):
|
||||||
|
global SSE_STREAMS
|
||||||
|
|
||||||
|
LOGGER.info("EventStream connected")
|
||||||
|
resp = await sse_response(request)
|
||||||
|
SSE_STREAMS.append(resp)
|
||||||
|
try:
|
||||||
|
await resp.wait()
|
||||||
|
finally:
|
||||||
|
LOGGER.info("EventStream disconnected")
|
||||||
|
SSE_STREAMS.remove(resp)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
async def start_http(queue):
|
||||||
|
ssl_c = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
ssl_c.check_hostname = False
|
||||||
|
ssl_c.load_cert_chain("certs/client.crt", "certs/client.key.u")
|
||||||
|
app = web.Application(middlewares=[middleware_auth])
|
||||||
|
app.add_routes([
|
||||||
|
#web.post("/ober/aan/", post_boot),
|
||||||
|
web.post("/ober/aan", post_boot),
|
||||||
|
web.get("/ober/stand-van-zaken", get_boot_stand_van_zaken),
|
||||||
|
web.get("/ober/", get_boot)
|
||||||
|
])
|
||||||
|
runner = web.AppRunner(app)
|
||||||
|
await runner.setup()
|
||||||
|
try:
|
||||||
|
site = web.TCPSite(runner, "0.0.0.0", 8086, ssl_context=ssl_c)
|
||||||
|
LOGGER.info("Started http server")
|
||||||
|
await site.start()
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
finally:
|
||||||
|
await runner.cleanup()
|
||||||
|
LOGGER.info("Stopped http server")
|
||||||
|
|
||||||
|
|
||||||
|
async def start_dbus(queue):
|
||||||
|
global QUEUE
|
||||||
|
QUEUE = queue
|
||||||
|
try:
|
||||||
|
server = await asyncio.start_server(aio_msgpack_rpc.Server(PcBridgeBoot(queue)), port=18002)
|
||||||
|
logging.info("RPC server started")
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logging.warn(f"Could not start RPC: {e}")
|
||||||
|
finally:
|
||||||
|
logging.info("Closing RPC")
|
||||||
|
if server:
|
||||||
|
server.close()
|
Loading…
Reference in a new issue