mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-21 20:13:05 +00:00
[21] Fix Gotify PR
This commit is contained in:
parent
fe9d0749bd
commit
33b92f2a2d
12 changed files with 116 additions and 142 deletions
|
@ -6,7 +6,7 @@ Updated python-dotenv requirement to 1.0.1
|
|||
|
||||
# New Features
|
||||
Added `monitor_existing` to sonarr and radarr. To update the monitored status of items existing in plex to match the `monitor` declared.
|
||||
|
||||
Added [Gotify](https://gotify.net/) as a notification service. Thanks @krstn420 for the initial code.
|
||||
|
||||
# Updates
|
||||
Added new [BoxOfficeMojo Builder](https://metamanager.wiki/en/latest/files/builders/mojo/) - credit to @nwithan8 for the suggestion and initial code submission
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.20.0-develop20
|
||||
1.20.0-develop21
|
||||
|
|
|
@ -107,7 +107,7 @@ notifiarr:
|
|||
apikey: ####################################
|
||||
gotify:
|
||||
url: http://192.168.1.12:80
|
||||
apikey: ####################################
|
||||
token: ####################################
|
||||
anidb: # Not required for AniDB builders unless you want mature content
|
||||
username: ######
|
||||
password: ######
|
||||
|
|
|
@ -10,13 +10,13 @@ Below is a `gotify` mapping example and the full set of attributes:
|
|||
```yaml
|
||||
gotify:
|
||||
url: ####################################
|
||||
apikey: ####################################
|
||||
token: ####################################
|
||||
```
|
||||
|
||||
| Attribute | Allowed Values | Required |
|
||||
|:----------|:-----------------------------------------|:------------------------------------------:|
|
||||
| `url` | Gotify Server Url | :fontawesome-solid-circle-check:{ .green } |
|
||||
| `apikey` | Gotify Application API Key | :fontawesome-solid-circle-check:{ .green } |
|
||||
| Attribute | Allowed Values | Required |
|
||||
|:----------|:-------------------------|:------------------------------------------:|
|
||||
| `url` | Gotify Server Url | :fontawesome-solid-circle-check:{ .green } |
|
||||
| `token` | Gotify Application Token | :fontawesome-solid-circle-check:{ .green } |
|
||||
|
||||
Once you have added the apikey your config.yml you have to add `gotify` to any [webhook](webhooks.md) to send that
|
||||
notification to Gotify.
|
||||
|
|
|
@ -27,7 +27,8 @@ webhooks:
|
|||
| [`changes`](#changes-notifications) | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } |
|
||||
|
||||
* Each Attribute can be either a webhook url as a string or a comma-separated list of webhooks urls.
|
||||
* To send notifications to [Notifiarr](notifiarr.md) or [Gotify](gotify.md) just add `notifiarr` or `gotify` to a webhook instead of the webhook url.
|
||||
* To send notifications to [Notifiarr](notifiarr.md) just add `notifiarr` to a webhook instead of the webhook url.
|
||||
* To send notifications to [Gotify](gotify.md) just add `gotify` to a webhook instead of the webhook url.
|
||||
|
||||
## Error Notifications
|
||||
|
||||
|
@ -77,7 +78,6 @@ level the error occurs.
|
|||
"error": str, // Error Message
|
||||
"critical": bool, // Critical Error
|
||||
"server_name": str, // Server Name
|
||||
"library_name": str, // Library Name
|
||||
"playlist": str // Playlist Name
|
||||
}
|
||||
```
|
||||
|
|
|
@ -293,13 +293,13 @@
|
|||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"apikey": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url",
|
||||
"apikey"
|
||||
"token"
|
||||
|
||||
],
|
||||
"title": "gotify"
|
||||
|
|
|
@ -474,7 +474,7 @@ notifiarr:
|
|||
apikey: this-is-a-placeholder-string
|
||||
gotify:
|
||||
url: http://192.168.1.12:80
|
||||
apikey: this-is-a-placeholder-string
|
||||
token: this-is-a-placeholder-string
|
||||
anidb: # Not required for AniDB builders unless you want mature content
|
||||
username: this-is-a-placeholder-string
|
||||
password: this-is-a-placeholder-string
|
||||
|
|
|
@ -528,32 +528,14 @@ class ConfigFile:
|
|||
else:
|
||||
logger.info("notifiarr attribute not found")
|
||||
|
||||
self.webhooks = {
|
||||
"error": check_for_attribute(self.data, "error", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"version": check_for_attribute(self.data, "version", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"run_start": check_for_attribute(self.data, "run_start", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"run_end": check_for_attribute(self.data, "run_end", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"changes": check_for_attribute(self.data, "changes", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"delete": check_for_attribute(self.data, "delete", parent="webhooks", var_type="list", default_is_none=True)
|
||||
}
|
||||
self.Webhooks = Webhooks(self, self.webhooks, notifiarr=self.NotifiarrFactory)
|
||||
try:
|
||||
self.Webhooks.start_time_hooks(self.start_time)
|
||||
if self.version[0] != "Unknown" and self.latest_version[0] != "Unknown" and self.version[1] != self.latest_version[1] or (self.version[2] and self.version[2] < self.latest_version[2]):
|
||||
self.Webhooks.version_hooks(self.version, self.latest_version)
|
||||
except Failed as e:
|
||||
logger.stacktrace()
|
||||
logger.error(f"Webhooks Error: {e}")
|
||||
|
||||
logger.save_errors = True
|
||||
logger.separator()
|
||||
|
||||
self.GotifyFactory = None
|
||||
if "gotify" in self.data:
|
||||
logger.info("Connecting to Gotify...")
|
||||
try:
|
||||
self.GotifyFactory = Gotify(self, {"url": check_for_attribute(self.data, "url", parent="gotify", throw=True),
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="gotify", throw=True)})
|
||||
self.GotifyFactory = Gotify(self, {
|
||||
"url": check_for_attribute(self.data, "url", parent="gotify", throw=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="gotify", throw=True)
|
||||
})
|
||||
except Failed as e:
|
||||
if str(e).endswith("is blank"):
|
||||
logger.warning(e)
|
||||
|
@ -572,7 +554,7 @@ class ConfigFile:
|
|||
"changes": check_for_attribute(self.data, "changes", parent="webhooks", var_type="list", default_is_none=True),
|
||||
"delete": check_for_attribute(self.data, "delete", parent="webhooks", var_type="list", default_is_none=True)
|
||||
}
|
||||
self.Webhooks = Webhooks(self, self.webhooks, notifiarr=self.GotifyFactory)
|
||||
self.Webhooks = Webhooks(self, self.webhooks, notifiarr=self.NotifiarrFactory, gotify=self.GotifyFactory)
|
||||
try:
|
||||
self.Webhooks.start_time_hooks(self.start_time)
|
||||
if self.version[0] != "Unknown" and self.latest_version[0] != "Unknown" and self.version[1] != self.latest_version[1] or (self.version[2] and self.version[2] < self.latest_version[2]):
|
||||
|
@ -1243,7 +1225,7 @@ class ConfigFile:
|
|||
logger.info("")
|
||||
logger.info(f"{display_name} library's Tautulli Connection {'Failed' if library.Tautulli is None else 'Successful'}")
|
||||
|
||||
library.Webhooks = Webhooks(self, {}, library=library, notifiarr=self.NotifiarrFactory or self.GotifyFactory,)
|
||||
library.Webhooks = Webhooks(self, {}, library=library, notifiarr=self.NotifiarrFactory, gotify=self.GotifyFactory)
|
||||
library.Overlays = Overlays(self, library)
|
||||
|
||||
logger.info("")
|
||||
|
|
|
@ -1,42 +1,99 @@
|
|||
from json import JSONDecodeError
|
||||
from modules import util
|
||||
from modules.util import Failed
|
||||
from retrying import retry
|
||||
|
||||
logger = util.logger
|
||||
|
||||
class Gotify:
|
||||
def __init__(self, config, params):
|
||||
self.config = config
|
||||
self.apikey = params["apikey"]
|
||||
self.url = params["url"]
|
||||
logger.secret(self.apikey)
|
||||
self.token = params["token"]
|
||||
self.url = params["url"].rstrip("/")
|
||||
logger.secret(self.url)
|
||||
logger.secret(self.token)
|
||||
try:
|
||||
self.request(path="message")
|
||||
except JSONDecodeError:
|
||||
raise Failed("Gotify Error: Invalid JSON response received")
|
||||
logger.info(f"Gotify Version: {self._request(path='version', post=False)['version']}")
|
||||
except Exception:
|
||||
logger.stacktrace()
|
||||
raise Failed("Gotify Error: Invalid URL")
|
||||
|
||||
def notification(self, json):
|
||||
return self.request(json=json)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
def request(self, json=None, path="message"):
|
||||
if not json:
|
||||
json = {
|
||||
"message": "Well hello there.",
|
||||
"priority": 1,
|
||||
"title": "This is first contact"
|
||||
}
|
||||
response = self.config.post(f"{self.url}{path}?token={self.apikey}", json=json)
|
||||
def _request(self, path="message", json=None, post=True):
|
||||
if post:
|
||||
response = self.config.post(f"{self.url}/{path}", headers={"X-Gotify-Key": self.token}, json=json)
|
||||
else:
|
||||
response = self.config.get(f"{self.url}/{path}")
|
||||
try:
|
||||
response_json = response.json()
|
||||
except JSONDecodeError as e:
|
||||
logger.error(response.content)
|
||||
logger.debug(e)
|
||||
raise e
|
||||
if response.status_code >= 400 or ("result" in response_json and response_json["result"] == "error"):
|
||||
logger.debug(f"Response: {response_json}")
|
||||
raise Failed(f"({response.status_code} [{response.reason}]) {response_json}")
|
||||
if not response_json["id"]:
|
||||
raise Failed("Gotify Error: Invalid apikey")
|
||||
return response
|
||||
if response.status_code >= 400:
|
||||
raise Failed(f"({response.status_code} [{response.reason}]) {response_json['errorDescription']}")
|
||||
return response_json
|
||||
|
||||
def notification(self, json):
|
||||
message = ""
|
||||
if json["event"] == "run_end":
|
||||
title = "Run Completed"
|
||||
message = f"Start Time: {json['start_time']}\n" \
|
||||
f"End Time: {json['end_time']}\n" \
|
||||
f"Run Time: {json['run_time']}\n" \
|
||||
f"Collections Created: {json['collections_created']}\n" \
|
||||
f"Collections Modified: {json['collections_modified']}\n" \
|
||||
f"Collections Deleted: {json['collections_deleted']}\n" \
|
||||
f"Items Added: {json['items_added']}\n" \
|
||||
f"Items Removed: {json['items_removed']}"
|
||||
if json["added_to_radarr"]:
|
||||
message += f"\n{json['added_to_radarr']} Movies Added To Radarr"
|
||||
if json["added_to_sonarr"]:
|
||||
message += f"\n{json['added_to_sonarr']} Movies Added To Sonarr"
|
||||
elif json["event"] == "run_start":
|
||||
title = "Run Started"
|
||||
message = json["start_time"]
|
||||
elif json["event"] == "version":
|
||||
title = "New Version Available"
|
||||
message = f"Current: {json['current']}\n" \
|
||||
f"Latest: {json['latest']}\n" \
|
||||
f"Notes: {json['notes']}"
|
||||
elif json["event"] == "delete":
|
||||
if "library_name" in json:
|
||||
title = "Collection Deleted"
|
||||
else:
|
||||
title = "Playlist Deleted"
|
||||
message = json["message"]
|
||||
else:
|
||||
new_line = "\n"
|
||||
if "server_name" in json:
|
||||
message += f"{new_line if message else ''}Server: {json['server_name']}"
|
||||
if "library_name" in json:
|
||||
message += f"{new_line if message else ''}Library: {json['library_name']}"
|
||||
if "collection" in json:
|
||||
message += f"{new_line if message else ''}Collection: {json['collection']}"
|
||||
if "playlist" in json:
|
||||
message += f"{new_line if message else ''}Playlist: {json['playlist']}"
|
||||
if json["event"] == "error":
|
||||
if "collection" in json:
|
||||
title_name = "Collection"
|
||||
elif "playlist" in json:
|
||||
title_name = "Playlist"
|
||||
elif "library_name" in json:
|
||||
title_name = "Library"
|
||||
else:
|
||||
title_name = "Global"
|
||||
title = f"{'Critical ' if json['critical'] else ''}{title_name} Error"
|
||||
message += f"{new_line if message else ''}Error Message: {json['error']}"
|
||||
else:
|
||||
title = f"{'Collection' if 'collection' in json else 'Playlist'} {'Created' if json['created'] else 'Modified'}"
|
||||
if json['radarr_adds']:
|
||||
message += f"{new_line if message else ''}{len(json['radarr_adds'])} Radarr Additions:"
|
||||
if json['sonarr_adds']:
|
||||
message += f"{new_line if message else ''}{len(json['sonarr_adds'])} Sonarr Additions:"
|
||||
message += f"{new_line if message else ''}{len(json['additions'])} Additions:"
|
||||
for add_dict in json['additions']:
|
||||
message += f"\n{add_dict['title']}"
|
||||
message += f"{new_line if message else ''}{len(json['removals'])} Removals:"
|
||||
for add_dict in json['removals']:
|
||||
message += f"\n{add_dict['title']}"
|
||||
|
||||
self._request(json={"message": message, "title": title})
|
||||
|
|
|
@ -16,8 +16,6 @@ class Library(ABC):
|
|||
self.Webhooks = None
|
||||
self.Operations = Operations(config, self)
|
||||
self.Overlays = None
|
||||
self.Notifiarr = None
|
||||
self.Gotify = None
|
||||
self.collections = []
|
||||
self.collection_names = []
|
||||
self.metadatas = []
|
||||
|
|
|
@ -5,7 +5,7 @@ from modules.util import Failed, YAML
|
|||
logger = util.logger
|
||||
|
||||
class Webhooks:
|
||||
def __init__(self, config, system_webhooks, library=None, notifiarr=None):
|
||||
def __init__(self, config, system_webhooks, library=None, notifiarr=None, gotify=None):
|
||||
self.config = config
|
||||
self.error_webhooks = system_webhooks["error"] if "error" in system_webhooks else []
|
||||
self.version_webhooks = system_webhooks["version"] if "version" in system_webhooks else []
|
||||
|
@ -14,6 +14,7 @@ class Webhooks:
|
|||
self.delete_webhooks = system_webhooks["delete"] if "delete" in system_webhooks else []
|
||||
self.library = library
|
||||
self.notifiarr = notifiarr
|
||||
self.gotify = gotify
|
||||
|
||||
def _request(self, webhooks, json):
|
||||
logger.trace("")
|
||||
|
@ -24,16 +25,15 @@ class Webhooks:
|
|||
for webhook in list(set(webhooks)):
|
||||
response = None
|
||||
logger.trace(f"Webhook: {webhook}")
|
||||
if webhook == "notifiarr" or webhook == "gotify":
|
||||
if webhook == "notifiarr":
|
||||
if self.notifiarr:
|
||||
for x in range(6):
|
||||
if webhook == "gotify":
|
||||
json = self.gotify(json)
|
||||
response = self.notifiarr.notification(json)
|
||||
else:
|
||||
response = self.notifiarr.notification(json)
|
||||
response = self.notifiarr.notification(json)
|
||||
if response.status_code < 500:
|
||||
break
|
||||
elif webhook == "gotify":
|
||||
if self.gotify:
|
||||
self.gotify.notification(json)
|
||||
else:
|
||||
if webhook.startswith("https://discord.com/api/webhooks"):
|
||||
json = self.discord(json)
|
||||
|
@ -44,17 +44,16 @@ class Webhooks:
|
|||
try:
|
||||
response_json = response.json()
|
||||
logger.trace(f"Response: {response_json}")
|
||||
if (webhook == "notifiarr" or webhook == "gotify") and self.notifiarr and response.status_code == 400:
|
||||
if webhook == "notifiarr" and self.notifiarr and response.status_code == 400:
|
||||
def remove_from_config(text, hook_cat):
|
||||
if response_json["details"]["response"] == text:
|
||||
yaml = YAML(self.config.config_path)
|
||||
changed = False
|
||||
if hook_cat in yaml.data and yaml.data["webhooks"][hook_cat]:
|
||||
if isinstance(yaml.data["webhooks"][hook_cat], list) and ("notifiarr" in yaml.data["webhooks"][hook_cat] or "gotify" in yaml.data["webhooks"][hook_cat]):
|
||||
if isinstance(yaml.data["webhooks"][hook_cat], list) and "notifiarr" in yaml.data["webhooks"][hook_cat]:
|
||||
changed = True
|
||||
yaml.data["webhooks"][hook_cat].pop("notifiarr")
|
||||
yaml.data["webhooks"][hook_cat].pop("gotify")
|
||||
elif yaml.data["webhooks"][hook_cat] == "notifiarr" or yaml.data["webhooks"][hook_cat] == "gotify":
|
||||
elif yaml.data["webhooks"][hook_cat] == "notifiarr":
|
||||
changed = True
|
||||
yaml.data["webhooks"][hook_cat] = None
|
||||
if changed:
|
||||
|
@ -67,7 +66,7 @@ class Webhooks:
|
|||
remove_from_config("PMM start/complete trigger is not enabled", "run_end")
|
||||
remove_from_config("PMM app updates trigger is not enabled", "version")
|
||||
if "result" in response_json and response_json["result"] == "error" and "details" in response_json and "response" in response_json["details"]:
|
||||
raise Failed(f"Notifiarr/Gotify Error: {response_json['details']['response']}")
|
||||
raise Failed(f"Notifiarr Error: {response_json['details']['response']}")
|
||||
if response.status_code >= 400 or ("result" in response_json and response_json["result"] == "error"):
|
||||
raise Failed(f"({response.status_code} [{response.reason}]) {response_json}")
|
||||
except JSONDecodeError:
|
||||
|
@ -332,65 +331,3 @@ class Webhooks:
|
|||
new_json["embeds"][0]["fields"] = fields
|
||||
return new_json
|
||||
|
||||
|
||||
def gotify(self, json: dict):
|
||||
message = ""
|
||||
if json.get("event") == "run_end":
|
||||
title = "Run Completed"
|
||||
message = f"Start Time: {json['start_time']}\nEnd Time: {json['end_time']}\nRun Time: {json['run_time']}\nCollections Created: {json['collections_created']}\nCollections Modified: {json['collections_modified']}\nCollections Deleted: {json['collections_deleted']}"
|
||||
if json.get("added_to_radarr"):
|
||||
message = message + (f"{json['added_to_radarr']} Movies Added To Radarr\n", None)
|
||||
if json.get("added_to_sonarr"):
|
||||
message = message + (f"{json['added_to_sonarr']} Series Added To Sonarr\n", None)
|
||||
elif json.get("event") == "run_start":
|
||||
title = "Run Started"
|
||||
message = json["start_time"]
|
||||
elif json.get("event") == "version":
|
||||
title = "New Version Available"
|
||||
message = f"Current : {json['current']}\nLatest: {json['latest']}\nNew Commits: {json['notes']}"
|
||||
else:
|
||||
message1 = ""
|
||||
text = ""
|
||||
if "server_name" in json:
|
||||
message1 = message1 + f"Server: {json['server_name']}\n"
|
||||
if "library_name" in json:
|
||||
message1 = message1 + f"Library: {json['library_name']}\n"
|
||||
if "collection" in json:
|
||||
text = "Collection"
|
||||
message1 = message1 + f"Collection: {json['collection']}\n"
|
||||
elif "playlist" in json:
|
||||
text = "Playlist"
|
||||
message1 = message1 + f"Playlist: {json['playlist']}\n"
|
||||
if message1:
|
||||
message1 = message1 + "\n"
|
||||
if json["event"] == "delete":
|
||||
title = json["message"]
|
||||
elif "error" in json:
|
||||
title = f"{'Critical ' if json['critical'] else ''}Error"
|
||||
message = message + f"Error Message: {json['error']}\n"
|
||||
else:
|
||||
title = f"{text} {'Created' if json['created'] else 'Modified'}"
|
||||
|
||||
def get_field_text(items_list):
|
||||
field_text = ""
|
||||
for i, item in enumerate(items_list, 1):
|
||||
field_text += f"\n{i}. {item['title']}"
|
||||
return field_text
|
||||
|
||||
if json["additions"]:
|
||||
message = message + f"Items Added: { get_field_text(json['additions'])}\n"
|
||||
if json["removals"]:
|
||||
message = message + f"Items Removed: { get_field_text(json['removals'])}\n"
|
||||
|
||||
gotify_json = {
|
||||
"message": "",
|
||||
"priority": 1,
|
||||
"title": ""
|
||||
}
|
||||
if message:
|
||||
gotify_json["message"] = message
|
||||
|
||||
if title:
|
||||
gotify_json["title"] = title
|
||||
|
||||
return gotify_json
|
||||
|
|
|
@ -927,11 +927,11 @@ def run_playlists(config):
|
|||
#logger.add_playlist_handler(playlist_log_name)
|
||||
status[mapping_name] = {"status": "Unchanged", "errors": [], "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
|
||||
server_name = None
|
||||
library_names = None
|
||||
try:
|
||||
builder = CollectionBuilder(config, playlist_file, mapping_name, playlist_attrs, extra=output_str)
|
||||
stats["names"].append(builder.name)
|
||||
logger.info("")
|
||||
server_name = builder.libraries[0].PlexServer.friendlyName
|
||||
|
||||
logger.separator(f"Running {mapping_name} Playlist", space=False, border=False)
|
||||
|
||||
|
@ -1049,7 +1049,7 @@ def run_playlists(config):
|
|||
except Deleted as e:
|
||||
logger.info(e)
|
||||
status[mapping_name]["status"] = "Deleted"
|
||||
config.notify_delete(e)
|
||||
config.notify_delete(e, server=server_name)
|
||||
except NotScheduled as e:
|
||||
logger.info(e)
|
||||
if str(e).endswith("and was deleted"):
|
||||
|
@ -1059,13 +1059,13 @@ def run_playlists(config):
|
|||
else:
|
||||
status[mapping_name]["status"] = "Not Scheduled"
|
||||
except Failed as e:
|
||||
config.notify(e, server=server_name, library=library_names, playlist=mapping_name)
|
||||
config.notify(e, server=server_name, playlist=mapping_name)
|
||||
logger.stacktrace()
|
||||
logger.error(e)
|
||||
status[mapping_name]["status"] = "PMM Failure"
|
||||
status[mapping_name]["errors"].append(e)
|
||||
except Exception as e:
|
||||
config.notify(f"Unknown Error: {e}", server=server_name, library=library_names, playlist=mapping_name)
|
||||
config.notify(f"Unknown Error: {e}", server=server_name, playlist=mapping_name)
|
||||
logger.stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
status[mapping_name]["status"] = "Unknown Error"
|
||||
|
|
Loading…
Reference in a new issue