Duplicati and Telegram

@ts678 @Igor_Goloma Okay I managed to implement an apprise api into my server. it works with simple curl, but the bash script doesn’t work in duplicati…it looks the following:

#!/bin/bash
curl -X POST -d '{"urls":"tgram://apikey", "body":"${PARSEDRESULT}, Operation: ${OPERATIONNAME}, Name: ${backup-name}", "title":"Duplicati_Rancher_Home"}' -H "Content-Type: application/json" http://apprise:8000/notify/ 

it works from bash, but not in duplicati…please help…

I don’t use Apprise, don’t use Telegram, and barely use curl, but these are some general comments:

It’s not clear what “works from bash” means. It wouldn’t ordinarily have those Duplicati variables in it.

If you’re trying to do environment variable substitution, this doesn’t occur inside single quoted strings such as the one given to POST -d. You can probably see what you built by echo of the line into a file.

The problem is, that I don’t get notified of anything…the variable substitution doesn’t work from bash “script name” in a Linux console…

How does duplicati call the script? Do I have to use bash script, or py script or php script?

Can you clarify? Is “script name” that two-liner, what environment variables are set, and why are you expecting them to be substituted inside the single quoted string (which doesn’t substitute variables)?

To quote the man page:

Enclosing characters in single quotes preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash.

Process.Start Method which I assume can start anything executable by its pathname (no arguments).

Sorry I’m not so much of a scripter…I expected them to be substituted, because I thought duplicati would do it…can you provide a curl command working for apprise? I maybe can help you with docs if we got it working so the whole community can use apprise :slight_smile: it’s the most extensive messenger on logs I’ve seen so far…as a result, you won’t have any development, and a whole lot of notification-functions :slight_smile: you than can provide a working script in the duplicati docs :slight_smile: I hope I can help with this…

Duplicati sets environment variables for a script, program, etc. to use however it’s written to use them. Duplicati won’t do the substitution for you. How could it do that if you were using a compiled program?

If you want to use a bash script, you can substitute inside double quotes, but I think the JSON will also require double quotes. Possibly there’s a better way, but one solution is to escape double quotes when they’re meant to be literal double quotes inside a double quoted string. It gets messy, but maybe it’d be

curl -X POST -d "{\"urls\":\"tgram://apikey\", \"body\":\"${PARSEDRESULT}, Operation: ${OPERATIONNAME}, Name: ${DUPLICATI__backup_name}\", \"title\":\"Duplicati_Rancher_Home\"}" -H "Content-Type: application/json" http://apprise:8000/notify/

You can test that by putting echo in front, and if you want to redirect to a file, add > /some/path at end.
Setting environment variables by hand for a test at your bash prompt can be done. Here’s an example:

$ export PARSEDRESULT="dummy PARSEDRESULT"
$ export OPERATIONNAME="dummy OPERATIONNAME"
$ export DUPLICATI__backup_name="dummy backup-name"
$ echo curl -X POST -d "{\"urls\":\"tgram://apikey\", \"body\":\"${PARSEDRESULT}, Operation: ${OPERATIONNAME}, Name: ${DUPLICATI__backup_name}\", \"title\":\"Duplicati_Rancher_Home\"}" -H "Content-Type: application/json" http://apprise:8000/notify/
curl -X POST -d {"urls":"tgram://apikey", "body":"dummy PARSEDRESULT, Operation: dummy OPERATIONNAME, Name: dummy backup-name", "title":"Duplicati_Rancher_Home"} -H Content-Type: application/json http://apprise:8000/notify/
$ 

Resources:

https://github.com/caronc/apprise-api/blob/master/README.md
https://github.com/duplicati/duplicati/blob/master/Duplicati/Library/Modules/Builtin/run-script-example.sh
in case you’re wondering why ${DUPLICATI__backup_name} is used in that, instead of $backup-name

Wow! Thanks for all the effort! I really appreciate it! :slight_smile: I"ll try the script and report if it works :slight_smile: Then I try to gonna write the steps down to get apprise working with docker :-)I really wanted to thank you for all the help @ts678! I hope I get it working :slight_smile:

@ts678 okay no matter what I did, I was not ble to get apprise working with script…duplicati always shows “you’ve got 1 warning” when I define script, but the logs show nothing…so I can’t debug…when I execute the script on host, everything works…

also here:

Viewing the Duplicati Server Logs at About --> Show log --> Live --> Warning might give some info. Technically the backup has already completed at script time, so the usual job log lacks script errors.

You could certainly do the echo-to-a-file test I’ve talked about several times. See how far script gets.
How quickly does the warning happen? There are lots of timeouts, but are you sure you got started?

How exactly? If you say bash <scriptname>, for example, it will work even if you forgot chmod +x.

I’m also still not hearing how you set up environment variables. Or does “everything works” mean with dummy settings like I used? One other issue I spotted in terms of working with Duplicati’s variables is:

The latter goes a step further than usual, because it adds an underscore inside PARSED_RESULT.

An issue I noticed with curl is that it tried writing its progress meter, which I didn’t really want in the scripted method (it makes more sense with a terminal), so I added --silent to the options for curl.

If you wonder how I saw what curl was writing, I used nc -l -p 8000, and had the curl script talk to it. Problem is that it wants a response to its POST. It can be typed in, but simpler test is to see POST:

$ nc -l -p 8000
POST /notify/ HTTP/1.1
Host: localhost:8000
User-Agent: curl/7.47.0
Accept: */*
Content-Type: application/json
Content-Length: 109

{"urls":"tgram://apikey", "body":"Success, Operation: Backup, Name: test2", "title":"Duplicati_Rancher_Home"}

Above was from a successful backup using the following script. Note I’m using localhost for the test.

$ ls -ln apprise.sh
-rwxr-xr-x 1 1000 1000 288 Apr  7 12:46 apprise.sh
$ cat apprise.sh
#!/bin/bash
curl --silent -X POST -d "{\"urls\":\"tgram://apikey\", \"body\":\"${DUPLICATI__PARSED_RESULT}, Operation: ${DUPLICATI__OPERATIONNAME}, Name: ${DUPLICATI__backup_name}\", \"title\":\"Duplicati_Rancher_Home\"}" -H "Content-Type: application/json" http://localhost:8000/notify/
$ 
1 Like

@ts678 OMG THANK YOU THANK YOU TAHNK YOU =) It’s working =) I’m very happy with the result =) Here is the step by step guide =):

1) Install docker and the aprise appi with the following config

version: '3.3'
services:

  apprise:
    image: caronc/apprise:latest

version: '3.3'
services:
  duplicati:
    image: linuxserver/duplicati:latest
    environment:
      PGID: '0'
      PUID: '0'
      TZ: Europe/Vienna
    ports:
     - 8200:8200
    volumes:
     - somevolume/config
     - backupvolume/backup
     - sourcevolume/source

2) Write a script for the aprise api (thanks to ts678 and all his effort getting this going) =)

#!/bin/bash
curl -s -X POST -d "{\"urls\":\"tgram://apikey/chatid/\", \"body\":\"${DUPLICATI__PARSED_RESULT} with ${DUPLICATI__OPERATIONNAME}, Backupname: ${DUPLICATI__backup_name}\", \"title\":\"Duplicati_Rancher_Home\"}" -H "Content-Type: application/json" http://apprise:8000/notify
exit 0

you can adjust the url to your notification-needs with this guide: Home · caronc/apprise Wiki · GitHub
so you can choose any notification you want! Be carefull with the json format…it’s very sensitive!
Just include exit 0 so duplicati doesn’t complain about exiting with error 255

3) Upload the script to /config/scripts directory INSIDE the linuxserver/duplicati docker-container
(If you scripted it on windows, install dos2unix (on wsl or on another unix machine) first, and convert the file before uploading!) use the following command:

dos2unix [nameofscript]

after uploading, set the script executable INSIDE the container:

docker exec duplicati chmod +x /conf/scripts/nameofscript

4) Specify a Backupjob with the following advanced options:

--run-script-after=/config/scripts/nameofscript

you can also specify it globally under the settings page, and set the advanced option there. If you do this, it’s applied to all your jobs.

5) ENJOY =)
Thanks again to @ts678 who putted a lot of effort in this =) and saarg from linuxserver/duplicati who helped here: Setup a --run-script-after in duplicati - Container Support - LinuxServer.io

1 Like

Hi,

I’ve tried to setup telegram notifications, too. Already had success with the following expert options:

–send-http-url=https://api.telegram.org/bot[TELEGRAM_BOT_ID]:[TELEGRAM_BOT_KEY]/sendMessage --send-http-extra-parameters=“chat_id=[TELEGRAM_CHAT_ID]” --send-http-message-parameter-name=text

My problem now is, it only emits “success” reports to the telegram chat. I’ve tried to delete a single ASCII character from a .dindex file on the target to simulate an error and it didn’t get sent over to Telegram. The Web UI of duplicati beta 2.0.5.1 (2020/01) displays the warning correct in the bottom red alert box telling me the backup failed.

How can I select to get all failures but no successes to the chat?

I’ve made this batch script for sending telegram messages on failure of a backup job. You just need to fill in your TELEGRAM_BOT_ID, TELEGRAM_BOT_APIKEY and TELEGRAM_CHAT_ID.

duplicati_post_telegram.cmd:

@echo off
setlocal enabledelayedexpansion
REM 
REM Script Configuration.
SET SERVICE_NAME=Duplicati
REM 
REM Runtime Variables.
SET SCRIPT_ENV_FULLFN="%~dpn0_env.cmd"
SET LOGFILE="%TEMP%\%~n0.log"
REM 
REM Validate env vars we got from Duplicati.
IF NOT DEFINED DUPLICATI__backup_name SET DUPLICATI__backup_name=API_ERR
IF NOT DEFINED DUPLICATI__PARSED_RESULT SET DUPLICATI__PARSED_RESULT=API_ERR
IF NOT DEFINED DUPLICATI__EVENTNAME SET DUPLICATI__EVENTNAME=API_ERR
IF NOT DEFINED DUPLICATI__OPERATIONNAME SET DUPLICATI__OPERATIONNAME=API_ERR
REM 
REM Prepare chat message.
SET "CHAT_MESSAGE=%SERVICE_NAME%: \"%DUPLICATI__backup_name%\" %DUPLICATI__PARSED_RESULT% %DUPLICATI__EVENTNAME% %DUPLICATI__OPERATIONNAME%"
REM 
REM Do not send chat message on SUCCESS.
IF /I "%DUPLICATI__PARSED_RESULT%" == "Success" call :logAdd "[INFO] Suppressed sending chat message [%CHAT_MESSAGE:\='%]" & goto :eof
call :logAdd "[INFO] Sending chat message [%CHAT_MESSAGE:\='%] ..."
REM 
REM Get command-line parameters
IF EXIST %SCRIPT_ENV_FULLFN% call %SCRIPT_ENV_FULLFN%
REM 
IF NOT DEFINED TELEGRAM_BOT_ID SET TELEGRAM_BOT_ID=%1
IF DEFINED TELEGRAM_BOT_ID SET TELEGRAM_BOT_ID=%TELEGRAM_BOT_ID:"=%
REM 
IF NOT DEFINED TELEGRAM_BOT_APIKEY SET TELEGRAM_BOT_APIKEY=%2
IF DEFINED TELEGRAM_BOT_APIKEY SET TELEGRAM_BOT_APIKEY=%TELEGRAM_BOT_APIKEY:"=%
REM 
IF NOT DEFINED TELEGRAM_CHAT_ID SET TELEGRAM_CHAT_ID=%3
IF DEFINED TELEGRAM_CHAT_ID SET TELEGRAM_CHAT_ID=%TELEGRAM_CHAT_ID:"=%
REM 
REM Validate command-line parameters.
IF NOT DEFINED TELEGRAM_BOT_ID call :logAdd "[ERROR] Parameter #1 TELEGRAM_BOT_ID is missing." & goto :eof
IF NOT DEFINED TELEGRAM_BOT_APIKEY call :logAdd "[ERROR] Parameter #2 TELEGRAM_BOT_APIKEY is missing." & goto :eof
IF NOT DEFINED TELEGRAM_CHAT_ID call :logAdd "[ERROR] Parameter #3 TELEGRAM_CHAT_ID is missing." & goto :eof
REM 
REM Send chat message.
powershell -NoLogo -NoProfile -ExecutionPolicy ByPass -Command "(Invoke-WebRequest -uri 'https://api.telegram.org/bot%TELEGRAM_BOT_ID%:%TELEGRAM_BOT_APIKEY%/sendMessage?chat_id=%TELEGRAM_CHAT_ID%&text=%CHAT_MESSAGE%').Content" 2>> %LOGFILE:.log=.err% | findstr /i /c:"\"ok\":true" && call :logAdd "[INFO] Successfully sent telegram notification." & goto :eof
call :logAdd "[ERROR] Failed to send telegram notification."
REM 
goto :eof


:logAdd
REM Syntax:
REM		logAdd [TEXT]
SET LOG_TEXT=%1
SET LOG_TEXT=%LOG_TEXT:"=%
SET LOG_DATETIMESTAMP=%DATE:~-4%-%DATE:~-7,-5%-%DATE:~-10,-8%_%time:~-11,2%:%time:~-8,2%:%time:~-5,2%
SET LOG_DATETIMESTAMP=%LOG_DATETIMESTAMP: =0%
echo %LOG_DATETIMESTAMP%: %LOG_TEXT%
echo %LOG_DATETIMESTAMP%: %LOG_TEXT% >> "%LOGFILE%"
goto :eof

duplicati_post_telegram_env.cmd:

@echo off
REM 
REM Script Configuration.
SET TELEGRAM_BOT_ID=XXX
SET TELEGRAM_BOT_APIKEY=YYY
SET TELEGRAM_CHAT_ID=ZZZ
REM 
goto :eof

It will look like this in Telegram Web:

image

And here is my Telegram Notifier bash script for Debian Linux. Don’t forget to chmod +x it before setting it to “run-script-after” in Duplicati.

#!/bin/bash
#
# Script Configuration.
DEBUG_MODE="0"
SCRIPT_FULLFN="$(basename "${0}")"
LOGFILE="/tmp/${SCRIPT_FULLFN%.*}.log"
LOG_MAX_LINES="1000"
SERVICE_NAME="Duplicati"
#
#
# -----------------
# --- Functions ---
# -----------------
logAdd ()
{
	TMP_DATETIME="$(date '+%Y-%m-%d [%H-%M-%S]')"
	TMP_LOGSTREAM="$(tail -n ${LOG_MAX_LINES} ${LOGFILE} 2>/dev/null)"
	echo "${TMP_LOGSTREAM}" > "$LOGFILE"
	if [ "$1" == "-q" ]; then
		#
		# Quiet mode.
		#
		echo "${TMP_DATETIME} ${@:2}" >> "${LOGFILE}"
	else
		#
		# Loud mode.
		#
		echo "${TMP_DATETIME} $*" | tee -a "${LOGFILE}"
	fi
	return
}
sendTelegramNotification ()
{
	#
	# Usage:			sendTelegramNotification "<PN_TEXT>"
	# Example:			sendTelegramNotification "Test push message"
	#
	# Returns:
	#
	# - "0" on FAILURE.
	# - "1" on SUCCESS.
	#
	# Consts.
	CURL_BIN="curl"
	CURL_TIMEOUT="3"
	# 
	# Telegram Bot
	STN_TELEGRAM_BOT_ID="TO_FILL"
	STN_TELEGRAM_BOT_APIKEY="TO_FILL"
	# 
	# 	cf
	STN_TELEGRAM_CHAT_ID="TO_FILL"
	#
	# Variables.
	#
	STN_TEXT="$1"
	STN_TEXT="${STN_TEXT//\"/\\\"}"
	#
	# Log message.
	#
	if [ "${DEBUG_MODE}" == "1" ]; then
		logAdd -q "${MY_SERVICE_NAME}::sendTelegramNotification() fired."
	fi
	#
	# Send push message to Telegram Bot Chat.
	if ( eval ${CURL_BIN} -q \
			--insecure \
			--max-time \""${CURL_TIMEOUT}\"" \
			 "\"https://api.telegram.org/bot${STN_TELEGRAM_BOT_ID}:${STN_TELEGRAM_BOT_APIKEY}/sendMessage?chat_id=${STN_TELEGRAM_CHAT_ID}&text=${STN_TEXT}\"" \
			 2> /dev/null \| grep -Fiq "\"ok\\\":true\"" ); then
		#
		# Log message.
		logAdd -q "${MY_SERVICE_NAME}::sendTelegramNotification: ... SUCCESS."
		# Return SUCCESS.
		#
		echo -n "1"
	else
		#
		# Log message.
		logAdd -q "${MY_SERVICE_NAME}::sendTelegramNotification: ... FAILED."
		#
		# Return FAILURE.
		echo -n "0"
	fi
	#
	return
}
# -----------------------------------------------------
# --------------- END OF FUNCTION BLOCK ---------------
# -----------------------------------------------------
#
# Script Main.
#
# Validate env vars we got from Duplicati.
if  [ -z "${DUPLICATI__backup_name}" ]; then
	export DUPLICATI__backup_name="API_ERR"
fi
#
if  [ -z "${DUPLICATI__PARSED_RESULT}" ]; then
	export DUPLICATI__PARSED_RESULT="API_ERR"
fi
#
if  [ -z "${DUPLICATI__EVENTNAME}" ]; then
	export DUPLICATI__EVENTNAME="API_ERR"
fi
#
if  [ -z "${DUPLICATI__OPERATIONNAME}" ]; then
	export DUPLICATI__OPERATIONNAME="API_ERR"
fi
#
if [ "${DEBUG_MODE}" == "1" ]; then
	env | grep "DUPLICATI__"
fi
#
# Prepare chat message.
CHAT_MESSAGE="${SERVICE_NAME}: \"${DUPLICATI__backup_name}\" ${DUPLICATI__PARSED_RESULT} ${DUPLICATI__EVENTNAME} ${DUPLICATI__OPERATIONNAME}"
#
# Do not send chat message on SUCCESS.
if [ "${DUPLICATI__PARSED_RESULT}" == "Success" ]; then
	logAdd "[INFO] Suppressed sending chat message [${CHAT_MESSAGE}]."
	exit 0
fi
logAdd "[INFO] Sending chat message [${CHAT_MESSAGE}] ..."
#
# Send chat message.
SEND_RESULT="$(sendTelegramNotification "${CHAT_MESSAGE}")"
if [ "${SEND_RESULT}" == "0" ]; then
	logAdd "[ERROR] Failed to send telegram notification."
	exit 99
fi
#
logAdd "[INFO] Successfully sent telegram notification."
exit 0

Hello Catfriend1!
Are two separated files? with ‘cmd’ extension? May I change the name of script files??

Hello @jonathancaldeira

the two cms files need to be placed in the same directory. You can rename them as long as you understood the relation given in the batch script between them.
E.g.
main.cmd
main_env.cmd

The _env script is just holding your personal config params. You could also put the _env SET lines into the main cmd file to have it all in one file. Replace the line

IF EXIST %SCRIPT_ENV_FULLFN% call %SCRIPT_ENV_FULLFN%

by the contents of the _env.cmd to do so.

1 Like

I was slow to understand that it only sends messages in case of failure, so I set it to send in all cases.
Thank you for your help!

1 Like

@bd8392 thanks for the instruction. Some additions:

  apprise:
    image: caronc/apprise:latest
    container_name: apprise
    ports:
      - 8000:8000
    volumes:
      - /var/lib/apprise/config:/config
    privileged: true

And you should grant the correct access rights for the config folder according to https://hub.docker.com/r/caronc/apprise

1 Like

Welcome to the forum @aleksej.kuznecow and thanks for contributing the addition.
It’s wonderful to see the community helping to build what is truly a community effort.

1 Like