Skip to content

Instantly share code, notes, and snippets.

@linuxgemini
Last active December 26, 2020 11:27
Show Gist options
  • Save linuxgemini/f9e67e8160fe4f4802e769e4fd3b975f to your computer and use it in GitHub Desktop.
Save linuxgemini/f9e67e8160fe4f4802e769e4fd3b975f to your computer and use it in GitHub Desktop.
Discord Webhook post-processing script for NZBGet
#!/usr/bin/env python
# Discord Webhook post-processing script for NZBGet
#
# Copyright (C) 2020 linuxgemini <nzbget@linuxgemini.space>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Revision$ rev3a-release
# $Date$ 2020-12-26
#
################################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Send Discord Webhook notification.
#
# This script sends Webhook notification when the job is done.
#
# NOTE: This script requires Python to be installed on your system.
################################################################################
### OPTIONS ###
# When to send the message (Always, OnFailure).
#SendWebhook=Always
# Discord Webhook URL.
#WebhookURL=https://discord.com/api/webhooks/111111111111111111/NO-TOKENS-HERE
# Webhook Author Name.
#WebhookAuthor=NZBGet
# Webhook Author Image.
#WebhookAuthorImage=https://i.imgur.com/43hnhwk.png
# UserID to mention when Webhook is fired. eg. 686700097488420875
#WebhookMentionID=
# To check connection parameters click the button.
#ConnectionTest@Send Test Webhook
### NZBGET POST-PROCESSING SCRIPT ###
################################################################################
import os
import re
import sys
import datetime
import json
try:
from urllib import request as urllib2 # python 3
from urllib.parse import quote
from xmlrpc.client import ServerProxy
except ImportError:
import urllib2 # python 2
from urllib2 import quote
from xmlrpclib import ServerProxy
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_NONE=95
# Check if the script is called from nzbget 15.0 or later
if not "NZBOP_NZBLOG" in os.environ:
print("*** NZBGet post-processing script ***")
print("This script is supposed to be called from nzbget (15.0 or later).")
sys.exit(POSTPROCESS_ERROR)
print("[DETAIL] Script successfully started")
sys.stdout.flush()
INT_RE = re.compile(r"^[-]?\d+$")
required_options = ("NZBPO_WEBHOOKURL", "NZBPO_WEBHOOKAUTHOR", "NZBPO_WEBHOOKAUTHORIMAGE")
for optname in required_options:
if (not optname in os.environ):
print('[ERROR] Option %s is missing in configuration file. Please check script setting' % optname[6:])
sys.exit(POSTPROCESS_ERROR)
# Check if the script is executed from settings page with a custom command
command = os.environ.get("NZBCP_COMMAND")
test_mode = command == "ConnectionTest"
if command != None and not test_mode:
print("[ERROR] Invalid command " + command)
sys.exit(POSTPROCESS_ERROR)
status = os.environ.get("NZBPP_STATUS") if not test_mode else "SUCCESS/ALL"
total_status = os.environ.get("NZBPP_TOTALSTATUS") if not test_mode else "SUCCESS"
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
# If any script fails the status of the item in the history is "WARNING/SCRIPT".
# This status however is not passed to pp-scripts in the env var "NZBPP_STATUS"
# because most scripts are independent of each other and should work even
# if a previous script has failed. But not in the case of E-Mail script,
# which should take the status of the previous scripts into account as well.
if total_status == "SUCCESS" and os.environ.get("NZBPP_SCRIPTSTATUS") == "FAILURE":
total_status = "WARNING"
status = "WARNING/SCRIPT"
success = total_status == "SUCCESS"
if success and os.environ.get("NZBPO_SENDWEBHOOK") == "OnFailure" and not test_mode:
print("[INFO] Skipping sending of message for successful download")
sys.exit(POSTPROCESS_NONE)
if success:
subject = '**Success** for "**%s**"' % (os.environ.get("NZBPP_NZBNAME", "Test download"))
text = 'Download of "**%s**" has successfully completed.' % (os.environ.get("NZBPP_NZBNAME", "Test download"))
else:
subject = '**Failure** for "**%s**"' % (os.environ["NZBPP_NZBNAME"])
text = 'Download of "**%s**" has failed.' % (os.environ["NZBPP_NZBNAME"])
text += '\nStatus: **%s**' % status
if (os.environ.get("NZBPO_SENDWEBHOOK") == "Always" or \
(os.environ.get("NZBPO_SENDWEBHOOK") == "OnFailure" and not success)) and \
not test_mode:
# To get statistics or the post-processing log we connect to NZBGet via XML-RPC.
# For more info visit http://nzbget.net/api
# First we need to know connection info: host, port and password of NZBGet server.
# NZBGet passes all configuration options to post-processing script as
# environment variables.
host = os.environ['NZBOP_CONTROLIP'];
port = os.environ['NZBOP_CONTROLPORT'];
username = os.environ['NZBOP_CONTROLUSERNAME'];
password = os.environ['NZBOP_CONTROLPASSWORD'];
if host == '0.0.0.0': host = '127.0.0.1'
# Build a URL for XML-RPC requests
rpcUrl = 'http://%s:%s@%s:%s/xmlrpc' % (quote(username), quote(password), host, port);
# Create remote server object
server = ServerProxy(rpcUrl)
if not test_mode:
# Find correct nzb in method listgroups
groups = server.listgroups(0)
nzbID = int(os.environ["NZBPP_NZBID"])
for nzbGroup in groups:
if nzbGroup["NZBID"] == nzbID:
break
text += '\n\nStatistics:';
# add download size
DownloadedSize = float(nzbGroup["DownloadedSizeMB"])
unit = " MB"
if DownloadedSize > 1024:
DownloadedSize = DownloadedSize / 1024 # GB
unit = ' GB'
text += '\nDownloaded size: %.2f' % (DownloadedSize) + unit
# add average download speed
DownloadedSizeMB = float(nzbGroup["DownloadedSizeMB"])
DownloadTimeSec = float(nzbGroup["DownloadTimeSec"])
if DownloadTimeSec > 0: # check x/0 errors
avespeed = (DownloadedSizeMB/DownloadTimeSec) # MB/s
unit = " MB/s"
if avespeed < 1:
avespeed = avespeed * 1024 # KB/s
unit = " KB/s"
text += '\nAverage download speed: %.2f' % (avespeed) + unit
def format_time_sec(sec):
Hour = sec/3600
Min = (sec - (sec/3600)*3600)/60
Sec = (sec - (sec/3600)*3600)%60
return '%d:%02d:%02d' % (Hour,Min,Sec)
# add times
text += "\nTotal time: " + format_time_sec(int(nzbGroup["DownloadTimeSec"]) + int(nzbGroup["PostTotalTimeSec"]))
text += "\nDownload time: " + format_time_sec(int(nzbGroup["DownloadTimeSec"]))
text += "\nVerification time: " + format_time_sec(int(nzbGroup["ParTimeSec"]) - int(nzbGroup["RepairTimeSec"]))
text += "\nRepair time: " + format_time_sec(int(nzbGroup["RepairTimeSec"]))
text += "\nUnpack time: " + format_time_sec(int(nzbGroup["UnpackTimeSec"]))
print("[DETAIL] Forming Message")
msg = '%s\n\n' % (subject)
msg += text
# Send webhook
print('[DETAIL] Sending webhook')
sys.stdout.flush()
try:
obje = {"content": msg}
if os.environ["NZBPO_WEBHOOKAUTHOR"] != "":
obje["username"] = os.environ["NZBPO_WEBHOOKAUTHOR"]
if os.environ["NZBPO_WEBHOOKAUTHORIMAGE"] != "":
obje["avatar_url"] = os.environ["NZBPO_WEBHOOKAUTHORIMAGE"]
if os.environ["NZBPO_WEBHOOKMENTIONID"] != "" and RepresentsInt(os.environ["NZBPO_WEBHOOKMENTIONID"]):
obje["content"] = 'CC: <@%s>\n\n%s' % (os.environ["NZBPO_WEBHOOKMENTIONID"], msg)
par = json.dumps(obje).encode("utf8")
req = urllib2.Request(os.environ["NZBPO_WEBHOOKURL"], par, {"content-type": "application/json"})
req.add_header("user-agent", "NZBGet Discord Webhook")
f = urllib2.urlopen(req)
f.close()
except Exception as err:
print("[ERROR] %s" % err)
sys.exit(POSTPROCESS_ERROR)
# All OK, returning exit status 'POSTPROCESS_SUCCESS' (int <93>) to let NZBGet know
# that our script has successfully completed.
sys.exit(POSTPROCESS_SUCCESS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment