Telebot - mehrstufige Nachrichten

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Neuling_19_
User
Beiträge: 2
Registriert: Donnerstag 12. September 2019, 15:50

Hallo,

kurz zu mir. Ich habe bis vor einem Monat noch keinerlei Sachen in Richtung programmieren unternommen.
Ich habe einen Raspberry Pi geschenkt bekommen und wollte damit ein bisschen rumspielen.
Ich kann deshalb auch nicht richtig programmieren, mein Code ist lediglich mehr oder weniger abgeschrieben bzw. was ich in anderen Codes verstanden habe, habe ich versucht für meinen zu nutzen.
Mit dieser Anleitung habe ich meinen Telegrambot erstellt: https://www.instructables.com/id/Set-up ... pberry-Pi/

Mein Problem ist nun:
Ich würde gern wie eine Art Untermenü aufrufen - z.B. ich schreibe dem Pi er soll herunterfahren und er "fragt" mich um wie viel Uhr und die gesendete Uhrzeit soll verwendet werden. (command 6 im code)
Ich habe auch schon mal gelesen, dass es die Möglichkeit gibt mit Buttons zu arbeiten die die Antwortmöglichkeiten zeigen aber nie richtig verstanden....
Ich habe schon eingies versucht aber nie hinbekommen, hoffe ihr könnt mir weiterhelfen.

Danke im Voraus :)

Code: Alles auswählen

import sys
import time
import random
import telepot
import os
import datetime
import urllib

time.sleep(20)          # Zeit bis Pi hochgefahren ist und WLAN steht

def start():            # Nachricht, wenn Pi hochgefahren ist
    chat_id = #xxxxxxxxx
    bot.sendMessage(chat_id, str("online"))

def getTime(value):
    if value == "Zeit":
        return '{:%H:%M:%S}'.format(datetime.datetime.now())
    elif value=="Stunde":
        return '{:%H}'.format(datetime.datetime.now())
    elif value == "Minute":
        return '{:%M}'.format(datetime.datetime.now())

def handle(msg):
    chat_id = msg ['chat']['id']
    command = msg ['text']
    Zeit = getTime("Zeit")
    print ('Resived: %s') % command

    if command == '/command1': # Zufallszahl
        print Zeit
        bot.sendMessage(chat_id, random.randint(1,100))
    elif command == '/command2': # aktuelle Zeit
        print Zeit
        bot.sendMessage(chat_id, (Zeit))
    elif command == '/command3': # Pi auschalten
        print Zeit
        bot.sendMessage(chat_id, ("fahre herunter"))
        print "herunterfahren"
        os.system("sudo shutdown -h now")
        sys.exit()
    elif command == '/command4': # Pi neustarten
        print Zeit
        bot.sendMessage(chat_id, ("starte neu"))
        print "Neustart"
        os.system("sudo shutdown -r now")
        sys.exit()
    elif command == '/command5': # Snapshot von Dax
        print Zeit
        urllib.urlretrieve("https://www.boerse-frankfurt.de/assets/dax_cam/daxcam_big.jpg?reload=0.02539507756348014","daxcam_big.jpg")
        time.sleep(3)
        bot.sendMessage(chat_id, ("Dax (realtime):"))
        bot.sendDocument (chat_id, document = open("/home/pi/daxcam_big.jpg"))
        time.sleep(3)
        os.remove("/home/pi/daxcam_big.jpg")


#-----------------------------------------------------------  

    elif command == '/command6': # mit Uhrzeit herunterfahren
        bot.sendMessage(chat_id, ("Um wie viel Uhr soll der Pi heruntergefahren werden?"))
                       # warten auf Eingabe der Uhrzeit
                        bot.sendMessage(chat_id,("herunterfahren um x Uhr"))

#-----------------------------------------------------------


bot = telepot.Bot('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
bot.message_loop(handle)

Stunde = getTime("Stunde")
Minute = getTime("Minute")

print "I am listening..."
print  "Startzeit:",Stunde,":",Minute,"Uhr"
print

start()

while 1:
    Zeit = getTime ("Zeit")
    Stunde = getTime ("Stunde")
    Minute = getTime ("Minute")
    hour = int(Stunde)
    min = int(Minute)
    time.sleep(10)

    if hour == 23 and min == 15: # Pi nach Uhrzeit herunterfahren
        print ("automatisch ausschalten")
        print (Zeit)
        chat_id = #xxxxxxxxxxxx
        bot.sendMessage(chat_id, str("Zeit zum Ausschalten"))
                time.sleep(10)
        os.system("sudo shutdown -h now")
        sys.exit()
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Neuling_19_: Du verwendest da Python 2 — das sollte man wirklich nicht mehr für neuen Code verwenden!

Am Anfang darauf zu hoffen das nach 20 Sekunden das WLAN da ist, ist keine gute Idee. Starte das wenn Du *weisst*, dass das WLAN da ist. Das ist ein Problem das ausserhalb des Programms gelöst werden sollte. Zum Beispiel über systemd.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Keine Variablen. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `get_time()` statt `getTime()`.

Diese Funktion ist aber sowieso etwas komisch. Je nach dem was man übergibt werden unterschiedliche Werte zurückgegeben, das sind also eigentlich drei verschiedene Funktionen. Und wenn man etwas übergibt was nicht erwartet wird, dann gibt die Funktion implizit `None` zurück. Die verwendung ist zudem fehlerhaft: Wenn man Stunde und Minute von *einem* Zeitpunkt haben will, dann darf man diesen Zeitpunkt auch nur *einmal* ermitteln. Wenn man Stunde und Minute mit *zwei* getrennten Aufrufen ermittelt, kann es sein, das die Werte falsch sind, weil zwischen den Aufrufen ja Zeit vergeht, und man so um den Stundenwechsel herum die Stunde nicht zu dem Minuten passen muss, so dass man beispielsweise 8 Uhr 0 Minute bekommt, obwohl es eigentlich 8 Uhr 59 sein müsste, also fast eine Stunde daneben liegt.

Aus einer Komponente eines `datetime`-Objekts erst eine Zeichenkette zu machen um die dann wieder in eine Zahl umzuwandeln ist auch unnötig umständlich. Überhaupt ist es nicht sinnvolle Stunde und Minute zu trennen wenn man sie zusammen in einem `datetime.time`-Objekt haben kann.

Die Dokumentation von `telepot` sagt das `Bot.message_loop()` „deprecated“ ist und in der Zukunft entfernt wird. Den Methodenaufruf sollte man also dringend ersetzen durch was auch immer da aktuell vorgesehen ist.

`str()` mit einer literalen Zeichenkette aufzurufen ist sinnfrei. Das ist ja bereits eine Zeichenkette. Genausowenig macht es sinn einfach Klammern um einzelne Werte zu setzen.

Python hat einen Datentyp für Wahrheitswerte (`bool`) mit den literalen Werten `True` und `False`. Man sollte dafür keine Zahlen missbrauchen.

`os.system()` sollte man nicht mehr verwenden, auch unter Python 2 schon nicht mehr. Dafür gibt es das `subprocess`-Modul.

Da jedes mal ``shutdown`` aufgerufen wird und jedes mal danach `sys.exit()`, sollte man den Aufruf in eine Funktion auslagern.

Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Die `handle()`-Funktion braucht also den `bot`. Hier kommt man noch mit `functools.partial()` aus, aber für das weitere Vorgehen, insbesondere wenn Du in einen Dialog mit dem Benutzer treten willst, wo man sich über mehrere `handle()`-Aufrufe Zustand merken muss, wird man um objektorientierte Programmierung (OOP) nicht herum kommen.

Man sollte keine Namen abkürzen. Wenn man `message` meint, sollte es nicht `msg` heissen.

Empfangen schreibt man "received".

Zeichenkettenformatierung mit ``%`` würde ich nicht mehr ohne guten Grund machen. Es gibt die `format()`-Methode und ab Python 3.6 f-Zeichenkettenliterale.

Die Kommandos sollten sinnvolle Namen bekommen, dann braucht man da auch keine Kommentare für.

Für das Bild verwendest Du einmal nur den Dateinamen und danach dann den kompletten Pfad. Das muss nicht das gleiche sein – also entweder alle Angaben relativ und sicherstellen, dass das Programm aus dem richtigen Verzeichnis heraus gestartet wird, oder alle Angaben absolut. Und man schreibt die selbe Information dann auch nicht *drei mal* in den Quelltext sondern definiert dafür eine Konstante. Bei einem aktuellen Python würde ich auch auf `pathlib` für Dateinamen und Pfade setzen.

Dateien die man öffnet, sollte man auch wieder schliessen. Dazu bietet sich die ``with``-Anweisung an um das sicherzustellen. Zudem muss man eine Bilddatei im Binärmodus öffnen und nicht als Textdatei.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import datetime as DateTime
from functools import partial
from pathlib import Path
import random
import subprocess
import sys
from time import sleep
from urllib.request import urlretrieve

import telepot

DAX_CAM_URL = (
    "https://www.boerse-frankfurt.de/assets/dax_cam/daxcam_big.jpg"
    "?reload=0.02539507756348014"
)
DAX_CAM_FILENAME = Path("/home/pi/daxcam_big.jpg")
TIME_FORMAT = "%H:%M:%S"


def call_shutdown(option):
    subprocess.run(["sudo", "shutdown", "--" + option, "now"], check=True)
    sys.exit()


def handle_message(bot, message):
    chat_id = message["chat"]["id"]
    command = message["text"]
    time = format(DateTime.now(), TIME_FORMAT)
    print("Received:", command)
    #
    # TODO Bessere Kommandonamen!
    # TODO Einzelne Funktionen/Methoden für die Kommandos.
    #
    if command == "/command1":  # Zufallszahl
        print(time)
        bot.sendMessage(chat_id, random.randint(1, 100))

    elif command == "/command2":  # aktuelle Zeit
        print(time)
        bot.sendMessage(chat_id, time)

    elif command == "/command3":  # Pi auschalten
        print(time)
        bot.sendMessage(chat_id, "fahre herunter")
        print("herunterfahren")
        call_shutdown("poweroff")

    elif command == "/command4":  # Pi neustarten
        print(time)
        bot.sendMessage(chat_id, "starte neu")
        print("Neustart")
        call_shutdown("reboot")

    elif command == "/command5":  # Snapshot von Dax
        print(time)
        urlretrieve(DAX_CAM_URL, DAX_CAM_FILENAME)
        sleep(3)
        bot.sendMessage(chat_id, "Dax (realtime):")
        with DAX_CAM_FILENAME.open("rb") as file:
            bot.sendDocument(chat_id, file)
        sleep(3)
        DAX_CAM_FILENAME.unlink()

    elif command == "/command6":  # mit Uhrzeit herunterfahren
        bot.sendMessage(
            chat_id, "Um wie viel Uhr soll der Pi heruntergefahren werden?"
        )
        # warten auf Eingabe der Uhrzeit
        bot.sendMessage(chat_id, "herunterfahren um x Uhr")


def main():
    bot = telepot.Bot("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
    #
    # FIXME Deprecated! See method's documentation.
    #
    bot.message_loop(partial(handle_message, bot))

    print("I am listening...")
    print(f"Startzeit: {DateTime.now():%H:%M} Uhr")
    print()
    chat_id = "#xxxxxxxxx"
    bot.sendMessage(chat_id, "online")
    while True:
        sleep(10)
        now = DateTime.now()
        if (now.hour, now.minute) == (23, 15):
            print("automatisch ausschalten")
            print(format(now, TIME_FORMAT))
            bot.sendMessage(chat_id, "Zeit zum Ausschalten")
            sleep(10)
            call_shutdown("poweroff")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Neuling_19_
User
Beiträge: 2
Registriert: Donnerstag 12. September 2019, 15:50

Danke dir!!!
Dann werde ich jetzt versuchen den Code in Python 3 zu schreiben (möglicherweise gingen auch deshalb bestimmt Funktionen von denen ich gelesen habe nicht). Danach arbeite ich deinen anderen Punkte durch.
Das wird vermutlich ein Weilchen dauern :D
Antworten