Send errors and missed reminders to webhook
New environment variable that you need to use for this to work.
This commit is contained in:
19
.env.example
19
.env.example
@ -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
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
73
poetry.lock
generated
@ -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"},
|
||||||
|
@ -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"
|
||||||
|
Reference in New Issue
Block a user