Skip to content

Commit

Permalink
Merge pull request #226 from khanxmetu/feature-ntfy
Browse files Browse the repository at this point in the history
Added support for ntfy notification service
  • Loading branch information
C0D3D3V authored Sep 24, 2024
2 parents 73fdf71 + 9b69d14 commit a42e4f8
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 1 deletion.
42 changes: 42 additions & 0 deletions moodle_dl/cli/notifications_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from moodle_dl.notifications.discord.discord_shooter import DiscordShooter
from moodle_dl.notifications.mail.mail_formater import create_full_welcome_mail
from moodle_dl.notifications.mail.mail_shooter import MailShooter
from moodle_dl.notifications.ntfy.ntfy_shooter import NtfyShooter
from moodle_dl.notifications.telegram.telegram_shooter import (
RequestRejectedError,
TelegramShooter,
Expand Down Expand Up @@ -170,6 +171,47 @@ def interactively_configure_discord(self) -> None:

self.config.set_property('discord', discord_cfg)

def interactively_configure_ntfy(self) -> None:
"Guides the user through the configuration of the ntfy notification."

do_ntfy = Cutie.prompt_yes_or_no('Do you want to activate Notifications via ntfy?')

if not do_ntfy:
self.config.remove_property('ntfy')
else:
print('[The following Inputs are not validated!]')
config_valid = False
while not config_valid:
topic = input('ntfy topic: ')
do_ntfy_server = Cutie.prompt_yes_or_no('Do you want to set a custom ntfy server?')
server = None
if do_ntfy_server:
server = input('ntfy server: ')

print('Testing server-Config...')

try:
ntfy_shooter = NtfyShooter(topic=topic, server=server)
ntfy_shooter.send(title='', message='This is a test message from moodle-dl!')

except (ConnectionError, RuntimeError) as e:
print(f'Error while sending the test message: {str(e)}')
continue

else:
input(
'Please check if you received the Testmessage.'
+ ' If yes, confirm with Return.\nIf not, exit'
+ ' this program ([CTRL]+[C]) and try again later.'
)
config_valid = True

ntfy_cfg = {'topic': topic}
if server:
ntfy_cfg['server'] = server

self.config.set_property('ntfy', ntfy_cfg)

def interactively_configure_xmpp(self) -> None:
"Guides the user through the configuration of the xmpp notification."

Expand Down
11 changes: 11 additions & 0 deletions moodle_dl/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def choose_task(config: ConfigHelper, opts: MoodleDlOpts):
NotificationsWizard(config, opts).interactively_configure_telegram()
elif opts.change_notification_discord:
NotificationsWizard(config, opts).interactively_configure_discord()
elif opts.change_notification_ntfy:
NotificationsWizard(config, opts).interactively_configure_ntfy()
elif opts.change_notification_xmpp:
NotificationsWizard(config, opts).interactively_configure_xmpp()
elif opts.config:
Expand Down Expand Up @@ -266,6 +268,15 @@ def _dir_path(path):
help=('Activate / deactivate / change the settings for receiving notifications via Discord.'),
)

group.add_argument(
'-cn',
'--change-notification-ntfy',
dest='change_notification_ntfy',
default=False,
action='store_true',
help=('Activate / deactivate / change the settings for receiving notifications via ntfy.'),
)

group.add_argument(
'-cx',
'--change-notification-xmpp',
Expand Down
3 changes: 2 additions & 1 deletion moodle_dl/notifications/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from moodle_dl.notifications.discord.discord_service import DiscordService
from moodle_dl.notifications.mail.mail_service import MailService
from moodle_dl.notifications.notification_service import NotificationService
from moodle_dl.notifications.ntfy.ntfy_service import NtfyService
from moodle_dl.notifications.telegram.telegram_service import TelegramService
from moodle_dl.notifications.xmpp.xmpp_service import XmppService

__all__ = ['ConsoleService', 'MailService', 'TelegramService', 'DiscordService', 'XmppService']
__all__ = ['ConsoleService', 'MailService', 'TelegramService', 'DiscordService', 'NtfyService', 'XmppService']

REMOTE_SERVICES = [
Class
Expand Down
Empty file.
61 changes: 61 additions & 0 deletions moodle_dl/notifications/ntfy/ntfy_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from typing import Optional, TypedDict

from moodle_dl.types import Course, File


class NtfyMessage(TypedDict):
title: str
message: str
source_url: Optional[str]


def _get_change_type(file: File) -> str:
if file.modified:
return "modified"
elif file.deleted:
return "deleted"
elif file.moved:
return "moved"
else:
return "new"


def create_full_moodle_diff_messages(changes: list[Course]) -> list[NtfyMessage]:
messages = []
for course in changes:
files_generic_msg = NtfyMessage()
misc_generic_msg = NtfyMessage()
files_generic_msg["message"] = f"{course.fullname}\n"
misc_generic_msg["message"] = f"{course.fullname}\n"
files_generic_cnt = 0
misc_generic_cnt = 0
for file in course.files:
change_type = _get_change_type(file)
if file.content_type == "description":
msg = NtfyMessage(
title=" ".join(file.content_filepath.split()[1:]),
message=f"{course.fullname}\n{file.content_filename}",
source_url=file.content_fileurl,
)
if change_type != "new":
msg["message"] += f"\nMessage {change_type}"
messages.append(msg)
elif file.content_type in ("file", "assignfile"):
msg_str = f"* {file.content_filename}"
if file.content_type == "assignfile":
msg_str += " | Assignment File"
if change_type != "new":
msg_str += f" | File {change_type}"
msg_str += "\n"
files_generic_msg["message"] += msg_str
files_generic_cnt += 1
else:
misc_generic_msg["message"] += f"* {file.content_filename}\n"
misc_generic_cnt += 1
files_generic_msg["title"] = f"{files_generic_cnt} File Changes"
misc_generic_msg["title"] = f"{misc_generic_cnt} Misc Changes"
if files_generic_cnt:
messages.append(files_generic_msg)
if misc_generic_cnt:
messages.append(misc_generic_msg)
return messages
69 changes: 69 additions & 0 deletions moodle_dl/notifications/ntfy/ntfy_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
import traceback
from typing import List

import moodle_dl.notifications.ntfy.ntfy_formatter as NF
from moodle_dl.downloader.task import Task
from moodle_dl.notifications.notification_service import NotificationService
from moodle_dl.notifications.ntfy.ntfy_shooter import NtfyShooter
from moodle_dl.types import Course


class NtfyService(NotificationService):
def _is_configured(self) -> bool:
# Checks if the sending of ntfy messages has been configured.
try:
self.config.get_property("ntfy")
return True
except ValueError:
logging.debug("ntfy-Notifications not configured, skipping.")
return False

def _send_messages(self, messages: List[str]):
"""
Sends an message
"""
if not self._is_configured() or messages is None or len(messages) == 0:
return

ntfy_cfg = self.config.get_property("ntfy")

logging.info("Sending Notification via ntfy...")
ntfy_shooter = NtfyShooter(ntfy_cfg["topic"], ntfy_cfg.get("server"))

for message in messages:
try:
ntfy_shooter.send(**message)
except BaseException as e:
logging.error(
"While sending notification:\n%s",
traceback.format_exc(),
extra={"exception": e},
)
raise e # to be properly notified via Sentry

def notify_about_changes_in_moodle(self, changes: List[Course]) -> None:
"""
Sends out a notification about the downloaded changes.
@param changes: A list of changed courses with changed files.
"""
if not self._is_configured():
return

messages = NF.create_full_moodle_diff_messages(changes)

self._send_messages(messages)

def notify_about_error(self, error_description: str):
"""
Sends out an error message if configured to do so.
@param error_description: The error object.
"""
pass

def notify_about_failed_downloads(self, failed_downloads: List[Task]):
"""
Sends out an message about failed download if configured to send out error messages.
@param failed_downloads: A list of failed Tasks.
"""
pass
20 changes: 20 additions & 0 deletions moodle_dl/notifications/ntfy/ntfy_shooter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import json
from typing import Optional

import requests


class NtfyShooter:
def __init__(self, topic: str, server: Optional[str] = None):
self.topic = topic
self.server = server or "https://ntfy.sh/"

def send(self, title: str, message: str, source_url: Optional[str] = None):
data = {"topic": self.topic, "title": title, "message": message}
if source_url:
data["click"] = source_url
view_action = {"action": "view", "label": "View", "url": source_url}
data.setdefault("actions", []).append(view_action)

resp = requests.post(self.server, data=json.dumps(data))
resp.raise_for_status()
1 change: 1 addition & 0 deletions moodle_dl/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ class MoodleDlOpts:
change_notification_mail: bool
change_notification_telegram: bool
change_notification_discord: bool
change_notification_ntfy: bool
change_notification_xmpp: bool
manage_database: bool
delete_old_files: bool
Expand Down

0 comments on commit a42e4f8

Please sign in to comment.