Send errors and missed reminders to webhook

New environment variable that you need to use for this to work.
This commit is contained in:
2022-10-31 01:24:32 +01:00
parent 6e040c243d
commit 17eada7e29
5 changed files with 121 additions and 12 deletions

View File

@ -1,15 +1,22 @@
# Discord bot token # Discord bot token
# https://discord.com/developers/applications
BOT_TOKEN=JFIiasfjioFIAOJFOIJIOSAF.AFo-7A.akwFakeopfaWPOKawPOFKOAKFPA BOT_TOKEN=JFIiasfjioFIAOJFOIJIOSAF.AFo-7A.akwFakeopfaWPOKawPOFKOAKFPA
# Timezone # Time zone that you are in. This is used when typing "Friday 22:00" and more.
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
# You want to use the TZ databse name. # You want to use the TZ databse name.
TIMEZONE=Europe/Stockholm TIMEZONE=Europe/Stockholm
# Optional: Change sqlite database location # Optional: Change sqlite database location.
# SQLITE_LOCATION=/jobs.sqlite # This will be created in the repo root (This is the default if not set) # SQLITE_LOCATION=/jobs.sqlite # This will be created in the repo root (This is the default if not set).
# SQLITE_LOCATION=/C:\\Users\\Jocke\\Desktop\\db.sqlite3 # Note the double backslashes and the first slash # SQLITE_LOCATION=/C:\\Users\\Jocke\\Desktop\\db.sqlite3 # Note the double backslashes and the first slash.
# SQLITE_LOCATION=//home/lovinator/foo.db # On Linux you will need to use double slashes before the path to get the absolute path # SQLITE_LOCATION=//home/lovinator/foo.db # On Linux you will need to use double slashes before the path to get the
# absolute path.
# Log level, CRITICAL, ERROR, WARNING, INFO, DEBUG # Log level, CRITICAL, ERROR, WARNING, INFO, DEBUG.
LOG_LEVEL=INFO LOG_LEVEL=INFO
# Webhook that discord-reminder-bot will send errors and information about missed reminders.
# https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
# Right click channel in Discord -> Intergrations -> Webhooks -> Create Webhook.
WEBHOOK_URL=https://discord.com/api/webhooks/582696524044304394/a3CMwZWchmHAXItB_lzSSRYBx0-AlPAHseJWqhHLfsAg_X4erac9-CeVeUDqPI1ac1vT

View File

@ -5,9 +5,12 @@ from typing import List
import dateparser import dateparser
import interactions import interactions
from apscheduler import events
from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_MISSED
from apscheduler.jobstores.base import JobLookupError from apscheduler.jobstores.base import JobLookupError
from apscheduler.triggers.date import DateTrigger from apscheduler.triggers.date import DateTrigger
from dateparser.conf import SettingValidationError from dateparser.conf import SettingValidationError
from discord_webhook import DiscordWebhook
from interactions import CommandContext, Embed, Option, OptionType, autodefer from interactions import CommandContext, Embed, Option, OptionType, autodefer
from interactions.ext.paginator import Paginator from interactions.ext.paginator import Paginator
@ -19,11 +22,27 @@ from discord_reminder_bot.settings import (
log_level, log_level,
scheduler, scheduler,
sqlite_location, sqlite_location,
webhook_url
) )
bot = interactions.Client(token=bot_token) bot = interactions.Client(token=bot_token)
def send_webhook(url=webhook_url, message: str = "discord-reminder-bot: Empty message."):
"""
Send a webhook to Discord.
Args:
url: Our webhook url, defaults to the one from settings.
message: The message that will be sent to Discord.
"""
if not url:
print("ERROR: Tried to send a webhook but you have no webhook url configured.")
return
webhook = DiscordWebhook(url=url, content=message, rate_limit_retry=True)
webhook.execute()
@bot.command(name="remind") @bot.command(name="remind")
async def base_command(ctx: interactions.CommandContext): async def base_command(ctx: interactions.CommandContext):
"""This description isn't seen in the UI (yet?) """This description isn't seen in the UI (yet?)
@ -639,6 +658,19 @@ async def remind_interval(
await ctx.send(message) await ctx.send(message)
def my_listener(event):
"""This gets called when something in APScheduler happens."""
if event.code == events.EVENT_JOB_MISSED:
# TODO: Is it possible to get the message?
scheduled_time = event.scheduled_run_time.strftime("%Y-%m-%d %H:%M:%S")
msg = f"Job {event.job_id} was missed! Was scheduled at {scheduled_time}"
send_webhook(message=msg)
if event.exception:
send_webhook(f"discord-reminder-bot failed to send message to Discord\n"
f"{event}")
async def send_to_discord(channel_id: int, message: str, author_id: int): async def send_to_discord(channel_id: int, message: str, author_id: int):
"""Send a message to Discord. """Send a message to Discord.
@ -647,8 +679,7 @@ async def send_to_discord(channel_id: int, message: str, author_id: int):
message: The message. message: The message.
author_id: User we should ping. author_id: User we should ping.
""" """
# TODO: Check if channel exists.
# TODO: Send message to webhook if channel is not found.
channel = await interactions.get( channel = await interactions.get(
bot, bot,
interactions.Channel, interactions.Channel,
@ -669,8 +700,8 @@ def start():
f"config_timezone = {config_timezone}\n" f"config_timezone = {config_timezone}\n"
f"log_level = {log_level}" f"log_level = {log_level}"
) )
scheduler.start() scheduler.start()
scheduler.add_listener(my_listener, EVENT_JOB_MISSED | EVENT_JOB_ERROR)
bot.start() bot.start()

View File

@ -9,7 +9,8 @@ load_dotenv(verbose=True)
sqlite_location = os.getenv("SQLITE_LOCATION", default="/jobs.sqlite") sqlite_location = os.getenv("SQLITE_LOCATION", default="/jobs.sqlite")
config_timezone = os.getenv("TIMEZONE", default="UTC") config_timezone = os.getenv("TIMEZONE", default="UTC")
bot_token = os.getenv("BOT_TOKEN", default="") bot_token = os.getenv("BOT_TOKEN", default="")
log_level = os.getenv(key="LOG_LEVEL", default="INFO") log_level = os.getenv("LOG_LEVEL", default="INFO")
webhook_url = os.getenv("WEBHOOK_URL", default="")
if not bot_token: if not bot_token:
raise ValueError("Missing bot token") raise ValueError("Missing bot token")

73
poetry.lock generated
View File

@ -78,6 +78,14 @@ docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
[[package]]
name = "certifi"
version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
[[package]] [[package]]
name = "charset-normalizer" name = "charset-normalizer"
version = "2.1.1" version = "2.1.1"
@ -157,7 +165,21 @@ readthedocs = ["Sphinx", "enum-tools[sphinx]", "furo", "sphinx-copybutton", "sph
type = "git" type = "git"
url = "https://github.com/interactions-py/library.git" url = "https://github.com/interactions-py/library.git"
reference = "unstable" reference = "unstable"
resolved_reference = "4cd4538f6d90b0b31471d7e5dcb98d68fc6d7987" resolved_reference = "596b95f8a97194920dfdb70a55b52493ff63554f"
[[package]]
name = "discord-webhook"
version = "0.17.0"
description = "execute discord webhooks"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
requests = ">=2.19.1"
[package.extras]
async = ["httpx (>=0.20.0)"]
[[package]] [[package]]
name = "exceptiongroup" name = "exceptiongroup"
@ -327,6 +349,24 @@ category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[package]]
name = "requests"
version = "2.28.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7, <4"
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<3"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "65.5.0" version = "65.5.0"
@ -412,6 +452,19 @@ tzdata = {version = "*", markers = "platform_system == \"Windows\""}
devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"]
test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"]
[[package]]
name = "urllib3"
version = "1.26.12"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]] [[package]]
name = "yarl" name = "yarl"
version = "1.8.1" version = "1.8.1"
@ -427,7 +480,7 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "c64bd02df0b561448367fecf47b590289f6c3f91cf6e0524e10f3d858854d8cc" content-hash = "d6a5cb22ff5db1181c1a877bf8bf13852d2f724dba691fc2ae26161b0ddbd8ab"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@ -535,6 +588,10 @@ attrs = [
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
] ]
certifi = [
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
{file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
charset-normalizer = [ charset-normalizer = [
{file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
{file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
@ -549,6 +606,10 @@ dateparser = [
] ]
dinteractions-Paginator = [] dinteractions-Paginator = []
discord-py-interactions = [] discord-py-interactions = []
discord-webhook = [
{file = "discord-webhook-0.17.0.tar.gz", hash = "sha256:bb47e5fc83f73614d7b2a3764b84359b52c96a94aadf3302bc3c067dd21b43cc"},
{file = "discord_webhook-0.17.0-py3-none-any.whl", hash = "sha256:d869849c4834f928f5c22597dc7600b1a30f9e797d1aeb1d4196711a243d9a73"},
]
exceptiongroup = [ exceptiongroup = [
{file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"},
{file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"},
@ -863,6 +924,10 @@ regex = [
{file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"}, {file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"},
{file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"},
] ]
requests = [
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
setuptools = [ setuptools = [
{file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"},
{file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"},
@ -926,6 +991,10 @@ tzlocal = [
{file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"},
{file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"},
] ]
urllib3 = [
{file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
{file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
yarl = [ yarl = [
{file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"},
{file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"},

View File

@ -29,6 +29,7 @@ sqlalchemy = "^1.4.42"
discord-py-interactions = { git = "https://github.com/interactions-py/library.git", rev = "unstable" } discord-py-interactions = { git = "https://github.com/interactions-py/library.git", rev = "unstable" }
interactions-wait-for = "^1.0.6" interactions-wait-for = "^1.0.6"
dinteractions-paginator = { git = "https://github.com/interactions-py/paginator.git", rev = "unstable" } dinteractions-paginator = { git = "https://github.com/interactions-py/paginator.git", rev = "unstable" }
discord-webhook = "^0.17.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^7.1.2" pytest = "^7.1.2"