@@ -23,7 +23,6 @@ class AppriseProvider(NotificationProvider):
2323 "apprise_slack" : "slack://{}" ,
2424 "apprise_gotify" : "gotify://{}" ,
2525 "apprise_matrix" : "matrix://{}" ,
26- "apprise_mqtt" : "mqtt://{}" ,
2726 "apprise_custom" : "{}"
2827 }
2928
@@ -47,28 +46,28 @@ def initialize(self, settings: CoreSettings, **kwargs) -> bool:
4746 for url in self .urls :
4847 add_result = self .apprise .add (url )
4948 if not add_result :
50- log (f"Failed to add Apprise URL: { url } " , LOG_STANDARD )
49+ log (f"Failed to add notification URL: { url } " , LOG_STANDARD )
5150 else :
5251 service_type = url .split ('://' )[0 ] if '://' in url else 'custom'
53- log (f"Added { service_type } notification service" , LOG_VERBOSE )
52+ log (f"Added { service_type } service" , LOG_VERBOSE )
5453
5554 if self .is_configured ():
5655 services = [url .split ('://' )[0 ] for url in self .urls if '://' in url ]
5756 service_counts = {}
5857 for service in services :
5958 service_counts [service ] = service_counts .get (service , 0 ) + 1
6059 service_summary = ', ' .join ([f"{ count } { name } " for name , count in service_counts .items ()])
61- log (f"Apprise ready with { len ( self . urls ) } service(s) : { service_summary } " , LOG_VERBOSE )
60+ log (f"Notification services ready : { service_summary } " , LOG_VERBOSE )
6261 return True
6362
64- log ("Apprise initialized but no valid service URLs were configured. " , LOG_STANDARD )
63+ log ("No valid notification services configured" , LOG_STANDARD )
6564 return False
6665
6766 except ImportError :
6867 log ("Apprise package not installed. Run: pip install apprise" )
6968 return False
7069 except Exception as e :
71- log (f"Error initializing Apprise : { e } " )
70+ log (f"Error initializing notification services : { e } " )
7271 return False
7372
7473 def _collect_urls_from_settings (self ) -> List [str ]:
@@ -82,18 +81,54 @@ def _collect_urls_from_settings(self) -> List[str]:
8281 for setting_attr , url_template in self .SERVICE_MAP .items ():
8382 value = getattr (settings , setting_attr , "" )
8483 if value and isinstance (value , str ):
85- if setting_attr == "apprise_custom" and "://" in value :
84+ if setting_attr == "apprise_email" and "=" in value and "://" not in value and (
85+ value .strip ().startswith (("user=" , "pass=" , "smtp=" , "port=" )) or
86+ any (param in value for param in ["user=" , "pass=" , "smtp=" , "port=" ])
87+ ):
88+
89+ if "from=" not in value :
90+ url = f"mailtos://_?{ value } &from=ChannelWatch"
91+ else :
92+ url = f"mailtos://_?{ value } "
93+
94+ elif setting_attr == "apprise_discord" and ("discord.com/api/webhooks/" in value or "discordapp.com/api/webhooks/" in value ):
95+
96+ try :
97+ parts = value .split ("/api/webhooks/" )
98+ if len (parts ) == 2 and "/" in parts [1 ]:
99+ webhook_parts = parts [1 ].split ("/" , 1 )
100+ if len (webhook_parts ) >= 2 :
101+ webhook_id , webhook_token = webhook_parts [0 ], webhook_parts [1 ]
102+
103+ if "?" in webhook_token :
104+ webhook_token = webhook_token .split ("?" , 1 )[0 ]
105+
106+ url = f"discord://{ webhook_id } /{ webhook_token } "
107+ else :
108+ url = url_template .format (value )
109+ log (f"Could not extract token from Discord webhook URL" , LOG_STANDARD )
110+ else :
111+ url = url_template .format (value )
112+ log (f"Invalid Discord webhook URL format" , LOG_STANDARD )
113+ except Exception as e :
114+ log (f"Error parsing Discord webhook URL: { e } " , LOG_STANDARD )
115+ url = url_template .format (value )
116+ elif setting_attr == "apprise_custom" and "://" in value :
86117 url = value
87118 else :
88119 url = url_template .format (value )
120+ if setting_attr == "apprise_email" and "from=" not in url :
121+ separator = '&' if '?' in url else '?'
122+ url = f"{ url } { separator } from=ChannelWatch"
123+ log (f"Added ChannelWatch as sender name for email" , LOG_VERBOSE )
89124 urls .append (url )
90125
91126 email_to = settings .apprise_email_to
92127 if email_to :
93128 updated_urls = []
94129 found_mailto = False
95130 for url in urls :
96- if url .startswith ("mailto://" ):
131+ if url .startswith (( "mailto://" , "mailtos://" ) ):
97132 separator = '&' if '?' in url else '?'
98133 updated_urls .append (f"{ url } { separator } to={ email_to } " )
99134 found_mailto = True
@@ -102,7 +137,7 @@ def _collect_urls_from_settings(self) -> List[str]:
102137 if found_mailto :
103138 urls = updated_urls
104139 else :
105- log ( "APPRISE_EMAIL_TO provided, but no APPRISE_EMAIL configured." , LOG_STANDARD )
140+ pass
106141
107142 return urls
108143
@@ -113,51 +148,121 @@ def is_configured(self) -> bool:
113148 # NOTIFICATION DELIVERY
114149
115150 def send_notification (self , title : str , message : str , ** kwargs ) -> bool :
116- """Delivers notification to all configured Apprise services with optional image attachment and timeout ."""
117- log (f"AP: Entering send_notification (Title : { title } ) " , level = LOG_VERBOSE )
151+ """Delivers notification to all configured services."""
152+ log (f"Sending notification : { title } " , level = LOG_VERBOSE )
118153 if not self .is_configured ():
119- log ("AP: Apprise not configured or no services enabled. " , level = LOG_VERBOSE )
154+ log ("No notification services configured " , level = LOG_VERBOSE )
120155 return False
121156
122157 success = False
123158 try :
124159 image_url = kwargs .get ('image_url' )
125- attach = [image_url ] if image_url else None
126- log (f"AP: Image URL: { image_url } , Attach: { attach } " , level = LOG_VERBOSE )
160+ discord_urls = []
161+ other_urls = []
162+
163+ for url in self .urls :
164+ if url .startswith ('discord://' ):
165+ discord_urls .append (url )
166+ else :
167+ other_urls .append (url )
168+
169+ apprise_module = importlib .import_module ('apprise' )
170+ if discord_urls :
171+ try :
172+ discord_success = False
173+ try :
174+ import requests
175+
176+ for discord_url in discord_urls :
177+ if discord_url .startswith ('discord://' ) and '/' in discord_url [10 :]:
178+ parts = discord_url [10 :].split ('/' , 1 )
179+ if len (parts ) == 2 :
180+ webhook_id , webhook_token = parts
181+ webhook_url = f"https://discord.com/api/webhooks/{ webhook_id } /{ webhook_token } "
182+ embed = {
183+ "title" : title ,
184+ "description" : message ,
185+ "color" : 3447003 ,
186+ }
187+ if image_url :
188+ embed ["image" ] = {"url" : image_url }
189+
190+ payload = {
191+ "username" : "ChannelWatch Bot" ,
192+ "content" : "" ,
193+ "embeds" : [embed ]
194+ }
195+
196+ log (f"Sending Discord notification" , level = LOG_VERBOSE )
197+ response = requests .post (webhook_url , json = payload )
198+
199+ if response .status_code == 204 :
200+ discord_success = True
201+ log (f"Discord notification sent successfully" , level = LOG_VERBOSE )
202+ else :
203+ log (f"Discord notification failed: { response .status_code } { response .text } " , level = LOG_STANDARD )
204+ except ImportError :
205+ log ("Requests library not available, using Apprise fallback for Discord" , level = LOG_STANDARD )
206+ discord_message = message
207+ discord_apprise = apprise_module .Apprise ()
208+ for url in discord_urls :
209+ discord_apprise .add (url )
210+
211+ try :
212+ body_format = apprise_module .NotifyFormat .TEXT if hasattr (apprise_module , 'NotifyFormat' ) else None
213+ except (ImportError , AttributeError ):
214+ body_format = None
215+ discord_success = discord_apprise .notify (
216+ title = title ,
217+ body = discord_message ,
218+ body_format = body_format ,
219+ attach = [image_url ] if image_url else None
220+ )
221+ if discord_success :
222+ log ("Discord notification sent via Apprise fallback" , level = LOG_VERBOSE )
223+ except Exception as e :
224+ log (f"Error sending Discord notification: { e } " , level = LOG_STANDARD )
225+ except Exception as e :
226+ log (f"Discord notification error: { e } " , level = LOG_STANDARD )
227+ discord_success = False
228+ else :
229+ discord_success = True
230+ if other_urls :
231+ try :
232+ try :
233+ body_format = apprise_module .NotifyFormat .HTML if 'NotifyFormat' in dir (apprise_module ) else None
234+ html_message = message .replace ("\n " , "<br />" )
235+ except (ImportError , AttributeError ):
236+ body_format = None
237+ html_message = message
238+ other_apprise = apprise_module .Apprise ()
239+ for url in other_urls :
240+ other_apprise .add (url )
241+ attach = [image_url ] if image_url else None
242+ other_success = other_apprise .notify (
243+ title = title ,
244+ body = html_message ,
245+ attach = attach ,
246+ body_format = body_format
247+ )
248+ if other_success :
249+ log ("Other notification services: delivery successful" , level = LOG_VERBOSE )
250+ else :
251+ log ("Other notification services: delivery failed" , level = LOG_STANDARD )
252+ except Exception as e :
253+ log (f"Error with other notification services: { e } " , level = LOG_STANDARD )
254+ other_success = False
255+ else :
256+ other_success = True
257+ success = (discord_success or other_success )
127258
128- try :
129- apprise_module = importlib .import_module ('apprise' )
130- body_format = apprise_module .NotifyFormat .HTML
131- html_message = message .replace ("\n " , "<br />" )
132- log (f"AP: Body format set to HTML." , level = LOG_VERBOSE )
133- except (ImportError , AttributeError ):
134- body_format = None
135- html_message = message
136- log ("AP: Could not set HTML format for Apprise, using default." , level = LOG_VERBOSE )
137-
138- apprise = cast_optional (self .apprise )
139- if not apprise :
140- log ("AP: Apprise object not initialized." , level = LOG_STANDARD )
141- return False
142- log (f"AP: Apprise object obtained. Preparing to call apprise.notify." , level = LOG_VERBOSE )
143-
144- log (f"AP: Calling apprise.notify (Title: { title } )" , level = LOG_STANDARD )
145- success = apprise .notify (
146- title = title ,
147- body = html_message ,
148- attach = attach ,
149- body_format = body_format
150- )
151- log (f"AP: Returned from apprise.notify (Result: { success } )" , level = LOG_STANDARD )
152-
153259 if success :
154- log ("AP: Apprise library reported success. " , level = LOG_VERBOSE )
260+ log ("Notification sent successfully " , level = LOG_VERBOSE )
155261 else :
156- log ("AP: Apprise library reported failure. Check Apprise logs if enabled. " , level = LOG_STANDARD )
262+ log ("All notification services failed " , level = LOG_STANDARD )
157263
158264 except Exception as e :
159- log (f"AP: Exception occurred during Apprise notification : { e } " , level = LOG_STANDARD )
265+ log (f"Notification error : { e } " , level = LOG_STANDARD )
160266 success = False
161267
162- log (f"AP: Exiting send_notification (Result: { success } )" , level = LOG_VERBOSE )
163268 return success
0 commit comments