Hallo zusammen,
mein Ziel ist es einen eigenen Discord Erinnerungsbot zu erstellen, der aus dem jeweiligen Eintrag aus der data.toml wöchentlich eine Erinnerung in Discord schickt. Die Uhrzeit und Wochentag lässt sich aus date entnehmen, wann das Ereignis stattfindet. Anschließend wird noch eine Vorlaufzeit (remind_offset) abgezogen, sodass die Erinnerung um x Minuten vor Stattfinden des Ereignisses abgeschickt wird. Um die Ereignisse innerhalb von Python zu erstellen und planen, verwende ich die Modul sched. Dieser Part funktioniert auch nach meinen Vorstellungen, solange die Ausgabe mit einem print() erfolgt und nicht die wirkliche Erinnerung über Discord gesendet wird. Ebenfalls funktioniert ein alleinstehendes Discord Skript, mit dem ich nach Start des Skriptes eine Testnachricht an den gewünschten Discord Server und Kanal sende. Mein Problem tritt auf sobald ich beides vereinen möchte, vermutlich auf Grund meiner mangelnden Kenntnisse von async. Bzw. diese beiden Befehle vertragen sich nicht: client.run(toml.load("config.toml")["dc_token"]) und scheduler.run(blocking=True). Ändere ich das blocking in False funktioniert es auch nicht mehr. Bei einer Kombination habe ich es zwar geschafft beides zu vereinen aber dann bekomme ich Fehler von async wie diese hier
[<coroutine object create_territory_capture at 0x04C19568>,
<coroutine object create_territory_capture at 0x04C190A8>,
<coroutine object create_territory_capture at 0x04C192E8>,
<coroutine object create_territory_capture at 0x04C190E8>,
<coroutine object create_territory_capture at 0x04C19168>,
<coroutine object create_territory_capture at 0x04C196A8>,
<coroutine object create_territory_capture at 0x04C196E8>]
C:\Users\***\Nextcloud\P_Python\tabellenanzeige\dc_kombi.py:60: RuntimeWarning: coroutine 'create_territory_capture' was never awaited
create_territory_captures_after_start(scheduler,
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0389CA00>
Traceback (most recent call last):
File "C:\Users\***\AppData\Local\Programs\Python\Python38-32\lib\asyncio\proactor_events.py", line 116, in __del__
File "C:\Users\***\AppData\Local\Programs\Python\Python38-32\lib\asyncio\proactor_events.py", line 108, in close
File "C:\Users\***\AppData\Local\Programs\Python\Python38-32\lib\asyncio\base_events.py", line 719, in call_soon
File "C:\Users\***\AppData\Local\Programs\Python\Python38-32\lib\asyncio\base_events.py", line 508, in _check_closed
RuntimeError: Event loop is closed
Display More
Zugehöriger Kombinationsversuch:
import datetime
import pprint
import toml
import sched
import time
import utils
import discord
REMIND_OFFSET = toml.load("data.toml")["remind_offset"]
WORK_CHANNEL_NAME = toml.load("config.toml")["channel"]
intents = discord.Intents.default()
client = discord.Client(intents=intents)
def search_work_channel(work_channel_name):
channels = client.get_all_channels()
work_channel = None
for channel in channels:
if str(channel) == work_channel_name:
work_channel = client.get_channel(channel.id)
break
return work_channel
async def send_reminder(scheduler, remind_offset, territory, text, target_ts, _):
workchannel = search_work_channel(WORK_CHANNEL_NAME)
print(f"Verteidigung {territory['system']} um {datetime.datetime.fromtimestamp(target_ts)}")
print(text)
pprint.pprint(create_territory_capture(scheduler, remind_offset, territory))
await workchannel.send("Hello World")
async def create_territory_capture(scheduler, remind_offset, territory):
target_ts = utils.get_target_ts(territory["date"], remind_time_offset=remind_offset)
remind_ts = utils.get_remind_ts(target_ts, remind_offset)
return scheduler.enterabs(remind_ts, 1, send_reminder,
(scheduler,
remind_offset,
territory,
"Text...",
target_ts,
datetime.datetime.fromtimestamp(target_ts)))
def create_territory_captures_after_start(scheduler, remind_offset, territories):
reminders = []
for territory in territories:
reminders.append(create_territory_capture(scheduler, remind_offset, territory))
pprint.pprint(reminders)
@client.event
async def on_ready():
main()
def main():
scheduler = sched.scheduler(time.time, time.sleep)
create_territory_captures_after_start(scheduler,
REMIND_OFFSET,
toml.load("data.toml")["territories"])
scheduler.run(blocking=True)
if __name__ == "__main__":
client.run(toml.load("config.toml")["dc_token"])
Display More
Auch habe ich schon damit begonnen blind await und async bei diversen Funktionen einzubauen, aber leider noch kein Erfolg.
Im Anschluss poste ich die getrennten Varianten, die soweit funktionieren, inkl der data.toml.
import datetime
import pprint
import toml
import sched
import time
import utils
REMIND_OFFSET = toml.load("data.toml")["remind_offset"]
def send_reminder(scheduler, remind_offset, territory, text, target_ts, _):
print(f"Verteidigung {territory['system']} um {datetime.datetime.fromtimestamp(target_ts)}")
print(text)
pprint.pprint(create_territory_capture(scheduler, remind_offset, territory))
def create_territory_capture(scheduler, remind_offset, territory):
target_ts = utils.get_target_ts(territory["date"], remind_time_offset=remind_offset)
remind_ts = utils.get_remind_ts(target_ts, remind_offset)
return scheduler.enterabs(remind_ts, 1, send_reminder,
(scheduler,
remind_offset,
territory,
"Text...",
target_ts,
datetime.datetime.fromtimestamp(target_ts)))
def create_territory_captures_after_start(scheduler, remind_offset, territories):
reminders = []
for territory in territories:
reminders.append(create_territory_capture(scheduler, remind_offset, territory))
pprint.pprint(reminders)
def main():
scheduler = sched.scheduler(time.time, time.sleep)
create_territory_captures_after_start(scheduler,
REMIND_OFFSET,
toml.load("data.toml")["territories"])
scheduler.run(blocking=True)
if __name__ == "__main__":
main()
Display More
import datetime
import pytz
def weekday_mapping(weekday):
weekdays = {
"Montag": 0,
"Dienstag": 1,
"Mittwoch": 2,
"Donnerstag": 3,
"Freitag": 4,
"Samstag": 5,
"Sonntag": 6,
}
return weekdays[weekday]
def get_target_datetime(date_string, current_datetime, remind_time_offset=0):
weekday = date_string.split()[0]
time_ = date_string.split()[1].split(":")
target_datetime_utc = datetime.datetime.today().replace(hour=int(time_[0]), minute=int(time_[1]),
second=0, microsecond=0,
tzinfo=pytz.utc)
target_datetime = target_datetime_utc.astimezone(pytz.timezone('Europe/Berlin'))
weekdaynumber = weekday_mapping(weekday)
one_day = datetime.timedelta(days=1)
while True:
if target_datetime.weekday() == weekdaynumber:
if target_datetime > current_datetime + datetime.timedelta(minutes=remind_time_offset):
break
else:
target_datetime = target_datetime + one_day
else:
target_datetime = target_datetime + one_day
return target_datetime
def get_remaining_time(date_string):
current_datetime_utc = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
current_datetime = current_datetime_utc.astimezone(pytz.timezone('Europe/Berlin'))
target_datetime = get_target_datetime(date_string, current_datetime)
remaining_time = target_datetime - current_datetime
return int(remaining_time.total_seconds() * 1000)
def get_target_ts(date_string, remind_time_offset=0):
current_datetime_utc = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
current_datetime = current_datetime_utc.astimezone(pytz.timezone('Europe/Berlin'))
target_datetime = get_target_datetime(date_string, current_datetime, remind_time_offset)
return target_datetime.timestamp()
def get_remind_ts(target_ts, remind_offset):
return (datetime.datetime.fromtimestamp(target_ts) - datetime.timedelta(minutes=remind_offset)).timestamp()
Display More
remind_offset = 120 # minutes
[[territories]]
system = "Test"
kategorie = 1
ressource = "Testxyz"
date = "Montag 16:16"
other_data = "Quantenteilchen, Forschungsverstärker"
priority = 0
[[territories]]
system = "Otima"
kategorie = 1
ressource = "Parstahl, Iso2"
date = "Mittwoch 10:00"
other_data = "Quantenteilchen, Forschungsverstärker"
priority = 0
Display More
import discord
import toml
WORK_CHANNEL_NAME = toml.load("config.toml")["channel"]
intents = discord.Intents.default()
client = discord.Client(intents=intents)
def search_work_channel(work_channel_name):
channels = client.get_all_channels()
work_channel = None
for channel in channels:
if str(channel) == work_channel_name:
work_channel = client.get_channel(channel.id)
break
return work_channel
async def send_reminder(text):
workchannel = search_work_channel(WORK_CHANNEL_NAME)
await workchannel.send(text)
@client.event
async def on_ready():
await send_reminder("Hello World")
if __name__ == "__main__":
client.run(toml.load("config.toml")["dc_token"])
Display More
Im Grunde muss ich es schaffen, dass die Funktion ab Zeile 12 in gebietserinnerung.py def send_reminder(scheduler, remind_offset, territory, text, target_ts, _): statt der print Ausgabe den gewünschten Inhalt in Discord sendet. Aktuell weiß ich aber nicht mehr weiter wie ich das kombinieren kann und hoffe auf eure Hilfe.