diff --git a/.gitignore b/.gitignore
index 9f10b34e1..d8a7c692f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,4 @@
.vscode
dist/**
!dist/setup.sh
-!dist/sr.sh
\ No newline at end of file
+!dist/sr.sh
diff --git a/cyphernodeconf_docker/features.json b/cyphernodeconf_docker/features.json
index 9045fde70..76b50f25f 100644
--- a/cyphernodeconf_docker/features.json
+++ b/cyphernodeconf_docker/features.json
@@ -18,5 +18,9 @@
{
"name": "Specter Cypherapp",
"value": "specter"
+ },
+ {
+ "name": "Telegram notifications",
+ "value": "telegram"
}
]
diff --git a/cyphernodeconf_docker/help.json b/cyphernodeconf_docker/help.json
index 9e41b6a37..9898f3a9e 100644
--- a/cyphernodeconf_docker/help.json
+++ b/cyphernodeconf_docker/help.json
@@ -48,5 +48,6 @@
"installer_mode": "Only one installation mode is supported, right now: local docker (self-hosted). Choose wisely ;-)",
"installer_cleanup": "Do you want to remove this configurator Docker image after installation? This would free about 150MB of disk space.",
"docker_mode": "Cyphernode Docker services can be run using Docker Swarm (https://docs.docker.com/engine/swarm/) or docker-compose (https://docs.docker.com/compose/overview/). Both will work, some users prefer one to another depending on deployment types, scalability, current framework, etc.",
+ "telegram": "Would you like to enable Telegram notifications - Some manual steps need to be carried once before first use. See /notifier_docker/sample-config.sh",
"__default__": ""
}
diff --git a/cyphernodeconf_docker/lib/app.js b/cyphernodeconf_docker/lib/app.js
index e9beae1d0..e3847d479 100644
--- a/cyphernodeconf_docker/lib/app.js
+++ b/cyphernodeconf_docker/lib/app.js
@@ -561,6 +561,13 @@ module.exports = class App {
clearnet: !this.isChecked('features', 'tor') || this.isChecked('clearnet', 'clearnet_lightning'),
tor_hostname: this.sessionData.tor_lightning_hostname
}
+ },
+ telegram: {
+ networks: ['cyphernodenet'],
+ docker: "cypernode/notifier",
+ extra:{
+ torified: this.torifyables.find(data => data.value === 'tor_telegram').checked,
+ }
}
}
diff --git a/cyphernodeconf_docker/prompters/040_tor.js b/cyphernodeconf_docker/prompters/040_tor.js
index 871be6c10..a65783770 100644
--- a/cyphernodeconf_docker/prompters/040_tor.js
+++ b/cyphernodeconf_docker/prompters/040_tor.js
@@ -66,6 +66,7 @@ module.exports = {
// - OTS Callbacks (webhooks)
// - Address Watches Callbacks (webhooks)
// - TXID Watches Callbacks (webhooks)
+// - Telegram
// Certain services can also use clearnet. What do you want to allow to use clearnet?
// - Bitcoin Node
diff --git a/cyphernodeconf_docker/prompters/300_notifier.js b/cyphernodeconf_docker/prompters/300_notifier.js
new file mode 100644
index 000000000..d1176a210
--- /dev/null
+++ b/cyphernodeconf_docker/prompters/300_notifier.js
@@ -0,0 +1,27 @@
+const chalk = require('chalk');
+
+const name = 'notifier';
+
+const capitalise = function( txt ) {
+ return txt.charAt(0).toUpperCase() + txt.substr(1);
+};
+
+const prefix = function() {
+ return chalk.green(capitalise(name)+': ');
+};
+
+const featureCondition = function(props) {
+ return props.features && props.features.indexOf( 'telegram' ) != -1;
+};
+
+module.exports = {
+ name: function() {
+ return name;
+ },
+ prompts: function( utils ) {
+ return [];
+ },
+ templates: function( props ) {
+ return [ 'notifier.env' ];
+ }
+};
diff --git a/cyphernodeconf_docker/schema/config-v0.2.6.json b/cyphernodeconf_docker/schema/config-v0.2.6.json
index 3fdb34cc0..7d27b4377 100644
--- a/cyphernodeconf_docker/schema/config-v0.2.6.json
+++ b/cyphernodeconf_docker/schema/config-v0.2.6.json
@@ -189,7 +189,8 @@
"lightning",
"otsclient",
"batcher",
- "specter"
+ "specter",
+ "telegram"
],
"title": "The feature",
"default": "",
@@ -198,7 +199,8 @@
"lightning",
"otsclient",
"batcher",
- "specter"
+ "specter",
+ "telegram"
]
}
},
@@ -217,7 +219,8 @@
"tor_otsoperations",
"tor_otswebhooks",
"tor_addrwatcheswebhooks",
- "tor_txidwatcheswebhooks"
+ "tor_txidwatcheswebhooks",
+ "tor_telegram"
],
"title": "The Torified feature",
"default": "",
@@ -228,7 +231,8 @@
"tor_otsoperations",
"tor_otswebhooks",
"tor_addrwatcheswebhooks",
- "tor_txidwatcheswebhooks"
+ "tor_txidwatcheswebhooks",
+ "tor_telegram"
]
}
},
diff --git a/cyphernodeconf_docker/templates/installer/config.sh b/cyphernodeconf_docker/templates/installer/config.sh
index 0936b433d..26f0abfea 100644
--- a/cyphernodeconf_docker/templates/installer/config.sh
+++ b/cyphernodeconf_docker/templates/installer/config.sh
@@ -1,6 +1,7 @@
INSTALLER_MODE=<%= installer_mode %>
BITCOIN_INTERNAL=<%= (bitcoin_mode==="internal"?'true':'false') %>
FEATURE_LIGHTNING=<%= (features.indexOf('lightning') != -1)?'true':'false' %>
+FEATURE_TELEGRAM=<%= (features.indexOf('telegram') != -1)?'true':'false' %>
FEATURE_BATCHER=<%= (features.indexOf('batcher') != -1)?'true':'false' %>
FEATURE_SPECTER=<%= (features.indexOf('specter') != -1)?'true':'false' %>
FEATURE_OTSCLIENT=<%= (features.indexOf('otsclient') != -1)?'true':'false' %>
@@ -20,6 +21,7 @@ TOR_TXID_WATCH_WEBHOOKS=<%= (torifyables && torifyables.indexOf('tor_txidwatches
TOR_TRAEFIK=<%= (torifyables && torifyables.indexOf('tor_traefik') !== -1)?'true':'false' %>
TOR_BITCOIN=<%= (torifyables && torifyables.indexOf('tor_bitcoin') !== -1)?'true':'false' %>
TOR_LIGHTNING=<%= (torifyables && torifyables.indexOf('tor_lightning') !== -1)?'true':'false' %>
+TOR_TELEGRAM=<%= (torifyables && torifyables.indexOf('tor_telegram') !== -1)?'true':'false' %>
<% } %>
DOCKER_MODE=<%= docker_mode %>
RUN_AS_USER=<%= run_as_different_user?username:'' %>
diff --git a/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
index 439d95041..526880f05 100644
--- a/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
+++ b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
@@ -280,11 +280,14 @@ services:
- .env/notifier.env
volumes:
- "<%= logs_datapath %>:/cnlogs"
+ - "<%= proxy_datapath %>/pgpass:/proxy/db/pgpass"
+ - container_monitor:/container_monitor
networks:
- cyphernodenet
- cyphernodeappsnet
depends_on:
- broker
+ - postgres
<% if ( docker_mode === 'swarm' ) { %>
deploy:
replicas: 1
diff --git a/cyphernodeconf_docker/templates/installer/testdeployment.sh b/cyphernodeconf_docker/templates/installer/testdeployment.sh
index 60755d217..dd52235e3 100644
--- a/cyphernodeconf_docker/templates/installer/testdeployment.sh
+++ b/cyphernodeconf_docker/templates/installer/testdeployment.sh
@@ -56,6 +56,7 @@ export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
# Will test if Cyphernode is fully up and running...
docker run --rm -it -v $current_path/testfeatures.sh:/testfeatures.sh \
-v <%= gatekeeper_datapath %>:/gatekeeper \
+-v ${current_path}/.cyphernodeconf/installer/config.sh:/config.sh \
-v $current_path:/dist \
-v cyphernode_container_monitor:/container_monitor:ro \
--network cyphernodenet eclipse-mosquitto:<%= mosquitto_version %> /testfeatures.sh
@@ -83,6 +84,11 @@ if [ "$EXIT_STATUS" -ne "0" ]; then
exit 1
fi
+if [ "$RUN_TELEGRAM_SETUP" -ne "0" ]; then
+ printf "\r\n\e[1;32mStarting Telegram setup\e[0m\n"
+ ../notifier_docker/script/start-tg-setup.sh
+fi
+
printf "\r\n\033[0;92mDepending on your current location and DNS settings, point your favorite browser to one of the following URLs to access Cyphernode's status page:\r\n"
printf "\r\n"
printf "\033[0;95m<% cns.forEach(cn => { %><%= ('https://' + cn + ':' + traefik_https_port + '/welcome\\r\\n') %><% }) %>\033[0m\r\n"
diff --git a/cyphernodeconf_docker/templates/installer/testfeatures.sh b/cyphernodeconf_docker/templates/installer/testfeatures.sh
index 0c516b29a..e2f9335b5 100644
--- a/cyphernodeconf_docker/templates/installer/testfeatures.sh
+++ b/cyphernodeconf_docker/templates/installer/testfeatures.sh
@@ -4,6 +4,8 @@ apk add --update --no-cache openssl curl jq coreutils postgresql > /dev/null
. /gatekeeper/keys.properties
+. ./config.sh
+
checkgatekeeper() {
echo -e "\r\n\e[1;36mTesting Gatekeeper...\e[0;32m" > /dev/console
@@ -121,6 +123,30 @@ checknotifier() {
return 0
}
+checknotifiertelegram() {
+ echo -en "\r\n\e[1;36mTesting Notifier Telegram... " > /dev/console
+ local response
+ local returncode
+ local msg
+
+ if [ "$TOR_TELEGRAM" = "true" ]; then
+ msg="{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramNoop\",\"tor\":true}"
+ else
+ msg="{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramNoop\"}"
+ fi
+
+ response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "$msg")
+ returncode=$?
+ [ "${returncode}" -ne "0" ] && echo -e "\e[1;31mNotifier Telegram needs to be configured" > /dev/console && return 115
+ http_code=$(echo "${response}" | jq -r ".http_code")
+ [ "${http_code}" -ge "400" ] && echo -e "\e[1;31mNotifier Telegram needs to be configured" > /dev/console && return 118
+ [ "${http_code}" -eq "0" ] && echo -e "\e[1;31mNotifier Telegram needs to be configured" > /dev/console && return 119
+
+ echo -e "\e[1;36mNotifier Telegram rocks!" > /dev/console
+
+ return 0
+}
+
checkots() {
echo -en "\r\n\e[1;36mTesting OTSclient... " > /dev/console
local rc
@@ -289,6 +315,7 @@ feature_status() {
# Let's first see if everything is up.
echo "EXIT_STATUS=1" > /dist/exitStatus.sh
+RUN_TELEGRAM_SETUP=0
#############################
# Ping containers and PROXY #
@@ -306,7 +333,7 @@ if [ "${returncode}" -ne "0" ]; then
workingproxy="false"
fi
else
- echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thourough tests..." > /dev/console
+ echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thorough tests..." > /dev/console
fi
# Let's now check each feature fonctionality...
@@ -405,6 +432,25 @@ fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Notifier error!')}"
+<% if (features.indexOf('telegram') != -1) { %>
+#############################
+# NOTIFIER TELEGRAM #
+#############################
+
+result="${result},{\"coreFeature\":true, \"name\":\"notifier telegram\",\"working\":"
+status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"notifier\") | .active")
+if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
+ checknotifiertelegram
+ returncode=$?
+ # Non critical error - run telegram setup at the end
+ [ "${returncode}" -ne "0" ] && RUN_TELEGRAM_SETUP=1 && returncode=0
+else
+ returncode=1
+fi
+finalreturncode=$((${returncode} | ${finalreturncode}))
+result="${result}$(feature_status ${returncode} 'Notifier Telegram error! - Please run Telegram setup - See doc/TELEGRAM.md')}"
+<% } %>
+
#############################
# PYCOIN #
#############################
@@ -500,6 +546,7 @@ result="{${result}]}"
echo "${result}" > /gatekeeper/installation.json
echo "EXIT_STATUS=${finalreturncode}" > /dist/exitStatus.sh
+echo "RUN_TELEGRAM_SETUP=$RUN_TELEGRAM_SETUP" >> /dist/exitStatus.sh
<% if (features.indexOf('tor') !== -1 && torifyables && torifyables.indexOf('tor_traefik') !== -1) { %>
echo "TOR_TRAEFIK_HOSTNAME=$(cat /dist/.cyphernodeconf/tor/traefik/hidden_service/hostname)" >> /dist/exitStatus.sh
diff --git a/cyphernodeconf_docker/templates/notifier/notifier.env b/cyphernodeconf_docker/templates/notifier/notifier.env
index d1283b99e..160e122ef 100644
--- a/cyphernodeconf_docker/templates/notifier/notifier.env
+++ b/cyphernodeconf_docker/templates/notifier/notifier.env
@@ -3,3 +3,12 @@ TRACING=1
TOR_HOST=tor
TOR_PORT=9050
<% } %>
+
+<% if ( features.indexOf('telegram') !== -1 ) { %>
+FEATURE_TELEGRAM=true
+<% if ( features.indexOf('tor') !== -1 && torifyables && torifyables.indexOf('tor_telegram') !== -1 ) { %>
+TOR_TELEGRAM=true
+<% } %>
+<% } %>
+
+PGPASSFILE=/proxy/db/pgpass
diff --git a/cyphernodeconf_docker/torifyables.json b/cyphernodeconf_docker/torifyables.json
index 87c201270..51c861baa 100644
--- a/cyphernodeconf_docker/torifyables.json
+++ b/cyphernodeconf_docker/torifyables.json
@@ -26,5 +26,9 @@
{
"name": "TXID Watches Callbacks (webhooks)",
"value": "tor_txidwatcheswebhooks"
+ },
+ {
+ "name": "Telegram",
+ "value": "tor_telegram"
}
]
diff --git a/dist/setup.sh b/dist/setup.sh
index 9f4b2b198..734cbb2b1 100755
--- a/dist/setup.sh
+++ b/dist/setup.sh
@@ -493,7 +493,6 @@ install_docker() {
copy_file $cyphernodeconf_filepath/otsclient/otsclient.env $current_path/.env/otsclient.env 1 $SUDO_REQUIRED
copy_file $cyphernodeconf_filepath/proxycron/proxycron.env $current_path/.env/proxycron.env 1 $SUDO_REQUIRED
-
if [[ $BITCOIN_INTERNAL == true ]]; then
if [ ! -d $BITCOIN_DATAPATH ]; then
step " [32mcreate[0m $BITCOIN_DATAPATH"
@@ -856,6 +855,15 @@ install_apps() {
next
fi
fi
+
+ if [[ $FEATURE_TELEGRAM == true ]]; then
+ step " [32menabled[0m Telegram - Manual configuration needed before first time use (Bot and Group creation, API key) - see doc/TELEGRAM.md"
+ next
+ else
+ step " [32mdisabled[0m Telegram"
+ next
+ fi
+
}
install() {
diff --git a/doc/TELEGRAM.md b/doc/TELEGRAM.md
new file mode 100644
index 000000000..c1ecfabce
--- /dev/null
+++ b/doc/TELEGRAM.md
@@ -0,0 +1,91 @@
+# Telegram integration in Cyphernode.
+
+The first time you run Cyphernode, you will get an error concerning Telegram beacause Telegram has to be setup with the next few steps. Go to [STEP 1]
+
+Build and setup Cyphernode - Choose to enable Telegram.
+
+==> START CYPHERNODE by running /dist/start.sh
+
+[STEP 1]: In directory cyphernode/notifier_docker/script, you will find the script `start-tg-setup.sh` to start the Telegram setup. It runs inside the notifier container with this command :
+ `docker exec -it $(docker ps -q -f "name=cyphernode_notifier") ./tgsetup.sh`
+
+Follow the steps of the installer - example output follows:
+
+In directory notifier_docker/script, run ** ./start-tg-setup.sh **
+
+Testing database before starting the configuration
+Database is alive
+
+Do you wish to configure Telegram for Cyphernode? [yn] yA
+
+dding the Telegram base URL in database config table cyphernode_props
+[sql] psql -qAtX -h postgres -U cyphernode -c "INSERT INTO ...."
+
+
+Please go into your Telegram App and start chatting with the @BotFather
+
+==> (Step 1) Enter @Botfather in the search tab and choose this bot
+
+==> Note, official Telegram bots have a blue checkmark beside their name
+
+==> (Step 2) Click “Start” to activate BotFather bot. In response, you receive a list of commands to manage bots
+
+==> (Step 3) Choose or type the /newbot command and send it
+
+==> @BotFather replies: Alright, a new bot. How are we going to call it? Please choose a name for your bot
+
+==> (Step 4) Choose a name for your bot. And choose a username for your bot — the bot can be found by its username in searches. The username must be unique and end with the word 'bot'
+
+==> After you choose a suitable name for your bot — the bot is created. You will receive a message with a link to your bot t.me/
+
+==> Cyphernode needs the generated token to access the API: Copy the line below following the message 'Use this token to access the HTTP API'
+
+Enter the token here: 5172851233:AAHkpd4T1ILyhXyqDelNnOTgFE4hl-AQSVM
+
+Telegram Setup will now try to obtain the chat ID from the Telgram server.
+
+To make this happen, please go into the Telegram App and send a message to the new bot
+
+Click on the link in the @BotFather's answer : Congratulations on your new bot. You will find it at t.me/your-new-bot.
+
+Trying to contact Telegram server...
+
+Reloading configs
+
+Sending message to Telegram [Tue May 3 16:29:03 UTC 2022]
+Ok. Done.
+
+
+
+===============================================================
+How it works :
+
+calling Telegram API
+ example :
+ https://api.telegram.org/bot+TELEGRAM_API_KEY/your-action
+ https://api.telegram.org/botTELEGRAM_API_KEY/getMe
+ returns:
+ {"ok":true,"result":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot","can_join_groups":true,"can_read_all_group_messages":false,"supports_inline_queries":false}}
+
+ Add your Bot to a group and then get updates to get the chat.ID in order to send messages to this group afterwards.
+ Below, the chat.id is chat.id:-TELEGRAM_CHAT_ID
+
+ https://api.telegram.org/botTELEGRAM_API_KEY/getUpdates
+
+ {"ok":true,"result":[{"update_id":701180871,
+ #"my_chat_member":{"chat":{"id":-TELEGRAM_CHAT_ID,"title":"Logging","type":"group","all_members_are_administrators":false},"from":{"id":1609436204,"is_bot":false,"first_name":"Roger","last_name":"Brulotte","username":"RogerBrulotte","language_code":"en"},"date":1635877254,"old_chat_member":{"user":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"status":"member"},"new_chat_member":{"user":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"status":"left"}}},{"update_id":701180872,
+ #"message":{"message_id":7,"from":{"id":1609436204,"is_bot":false,"first_name":"Roger","last_name":"Brulotte","username":"RogerBrulotte","language_code":"en"},"chat":{"id":-TELEGRAM_CHAT_ID,"title":"Logging","type":"group","all_members_are_administrators":true},"date":1635877254,"left_chat_participant":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"left_chat_member":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"}}},{"update_id":701180873,
+ #"my_chat_member":{"chat":{"id":-TELEGRAM_CHAT_ID,"title":"Logging","type":"group","all_members_are_administrators":true},"from":{"id":1609436204,"is_bot":false,"first_name":"Roger","last_name":"Brulotte","username":"RogerBrulotte","language_code":"en"},"date":1635877290,"old_chat_member":{"user":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"status":"left"},"new_chat_member":{"user":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"status":"member"}}},{"update_id":701180874,
+ #"message":{"message_id":8,"from":{"id":1609436204,"is_bot":false,"first_name":"Roger","last_name":"Brulotte","username":"RogerBrulotte","language_code":"en"},"chat":{"id":-TELEGRAM_CHAT_ID,"title":"Logging","type":"group","all_members_are_administrators":true},"date":1635877290,"new_chat_participant":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"new_chat_member":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"new_chat_members":[{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"}]}}]}
+
+Bot says Hello World using the chat id returned previously
+ https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHAT-ID&text=Hello+World
+ https://api.telegram.org/botTELEGRAM_API_KEY/sendMessage?chat_id=-TELEGRAM_CHAT_ID&text=Hello+World
+
+ returns:
+ {"ok":true,"result":{"message_id":9,"from":{"id":2084591315,"is_bot":true,"first_name":"Roger-logger","username":"RogerLoggerBot"},"chat":{"id":-TELEGRAM_CHAT_ID,"title":"Logging","type":"group","all_members_are_administrators":true},"date":1635877783,"text":"Hello World"}}
+
+
+
+curl POST example
+ curl -X POST https://api.telegram.org/botTELEGRAM_API_KEY/sendMessage?chat_id=TELEGRAM_CHAT_ID -H 'Content-Type: application/json' -d '{"text":"text in POST data"}'
diff --git a/notifier_docker/Dockerfile b/notifier_docker/Dockerfile
index 2b858a8cd..76cb08b2a 100644
--- a/notifier_docker/Dockerfile
+++ b/notifier_docker/Dockerfile
@@ -2,7 +2,7 @@ FROM eclipse-mosquitto:1.6-openssl
ENV HOME /notifier
-RUN apk --no-cache --update add jq curl su-exec
+RUN apk --no-cache --update add jq curl su-exec postgresql
WORKDIR ${HOME}
diff --git a/notifier_docker/script/colors.sh b/notifier_docker/script/colors.sh
new file mode 100644
index 000000000..883d58eeb
--- /dev/null
+++ b/notifier_docker/script/colors.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# Reset
+Color_Off="\033[0m" # Text Reset
+
+# Regular Colors
+Black="\033[0;30m" # Black
+Red="\033[0;31m" # Red
+Green="\033[0;32m" # Green
+Yellow="\033[0;33m" # Yellow
+Blue="\033[0;34m" # Blue
+Purple="\033[0;35m" # Purple
+Cyan="\033[0;36m" # Cyan
+White="\033[0;37m" # White
+
+# Bold
+BBlack="\033[1;30m" # Black
+BRed="\033[1;31m" # Red
+BGreen="\033[1;32m" # Green
+BYellow="\033[1;33m" # Yellow
+BBlue="\033[1;34m" # Blue
+BPurple="\033[1;35m" # Purple
+BCyan="\033[1;36m" # Cyan
+BWhite="\033[1;37m" # White
+
+# Underline
+UBlack="\033[4;30m" # Black
+URed="\033[4;31m" # Red
+UGreen="\033[4;32m" # Green
+UYellow="\033[4;33m" # Yellow
+UBlue="\033[4;34m" # Blue
+UPurple="\033[4;35m" # Purple
+UCyan="\033[4;36m" # Cyan
+UWhite="\033[4;37m" # White
+
+# Background
+On_Black="\033[40m" # Black
+On_Red="\033[41m" # Red
+On_Green="\033[42m" # Green
+On_Yellow="\033[43m" # Yellow
+On_Blue="\033[44m" # Blue
+On_Purple="\033[45m" # Purple
+On_Cyan="\033[46m" # Cyan
+On_White="\033[47m" # White
+
+# High Intensty
+IBlack="\033[0;90m" # Black
+IRed="\033[0;91m" # Red
+IGreen="\033[0;92m" # Green
+IYellow="\033[0;93m" # Yellow
+IBlue="\033[0;94m" # Blue
+IPurple="\033[0;95m" # Purple
+ICyan="\033[0;96m" # Cyan
+IWhite="\033[0;97m" # White
+
+# Bold High Intensty
+BIBlack="\033[1;90m" # Black
+BIRed="\033[1;91m" # Red
+BIGreen="\033[1;92m" # Green
+BIYellow="\033[1;93m" # Yellow
+BIBlue="\033[1;94m" # Blue
+BIPurple="\033[1;95m" # Purple
+BICyan="\033[1;96m" # Cyan
+BIWhite="\033[1;97m" # White
+
+# High Intensty backgrounds
+On_IBlack="\033[0;100m" # Black
+On_IRed="\033[0;101m" # Red
+On_IGreen="\033[0;102m" # Green
+On_IYellow="\033[0;103m" # Yellow
+On_IBlue="\033[0;104m" # Blue
+On_IPurple="\033[10;95m" # Purple
+On_ICyan="\033[0;106m" # Cyan
+On_IWhite="\033[0;107m" # White
diff --git a/notifier_docker/script/requesthandler.sh b/notifier_docker/script/requesthandler.sh
index 5a571025f..45cebbc33 100644
--- a/notifier_docker/script/requesthandler.sh
+++ b/notifier_docker/script/requesthandler.sh
@@ -3,14 +3,28 @@
. ./trace.sh
. ./web.sh
. ./response.sh
+. ./sql.sh
+
main() {
trace "Entering main()..."
+ while true; do
+ loadConfig
+
+ readLoop
+ done
+
+}
+
+readLoop(){
local msg
local cmd
local response
local response_topic
+ local url
+
+ trace "[readLoop] Starting"
# Messages should have this form:
# {"response-topic":"response/5541","cmd":"web","url":"2557df870b9a:1111/callback1conf","body":"eyJpZCI6IjUxIiwiYWRkc...dCI6MTUxNzYwMH0K"}
@@ -29,9 +43,90 @@ main() {
response=$(web "${msg}")
publish_response "${response}" "${response_topic}" ${?}
;;
+ sendToTelegramGroup)
+ # example:
+ # local body=$(echo "{\"text\":\"Hello world in Telegram at `date -u +"%FT%H%MZ"`\"}" | base64)
+ # response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramGroup\",\"body\":\"${body}\"}")
+ if [ "${FEATURE_TELEGRAM}" = "true" ]; then
+ url=$(echo ${TG_BOT_URL}${TG_API_KEY}/sendMessage?chat_id=${TG_CHAT_ID})
+ trace "[main] telegram-url=${url}"
+
+ msg=$(echo ${msg} | jq --arg url ${url} '. += {"url":$url}' )
+ trace "[main] web-msg=${msg}"
+
+ response=$(web "${msg}")
+ publish_response "${response}" "${response_topic}" ${?}
+ else
+ trace "[main] Telegram is NOT enabled - message not sent"
+ fi
+ ;;
+ sendToTelegramNoop)
+ if [ "${FEATURE_TELEGRAM}" = "true" ]; then
+ url=$(echo ${TG_BOT_URL}${TG_API_KEY}/getMe)
+ trace "[main] telegram-url=${url}"
+
+ msg=$(echo ${msg} | jq --arg url ${url} '. += {"url":$url}' )
+ trace "[main] web-msg=${msg}"
+
+ response=$(web "${msg}")
+ publish_response "${response}" "${response_topic}" ${?}
+ else
+ trace "[main] Telegram is NOT enabled - message not sent"
+ fi
+ ;;
+ reloadConfig)
+ trace "[main] Reloading configs Now"
+ response="{\"return_code\":\"(loadConfig)\"}"
+ trace "[main] response=${response}"
+ publish_response "${response}" "${response_topic}" ${?}
+ trace "[main] Reloading configs - Done"
+
+ # restart read loop
+ break
+ ;;
esac
trace "[main] msg processed"
done
+
+ trace "[readLoop] Exiting read loop"
+}
+
+loadConfig(){
+ if [ "${FEATURE_TELEGRAM}" = "true" ]; then
+ trace "[loadConfig] FEATURE_TELEGRAM is ENABLED"
+
+ if [ "${TOR_TELEGRAM}" = "true" ]; then
+ trace "[loadConfig] Telegram will be used with tor"
+ fi
+
+ # wait for table to exist in DB - for clean install
+ waitfortable "cyphernode_props"
+
+ trace "[loadConfig] Looking up TG_BOT_URL in database"
+
+ TG_BOT_URL=$(sql "SELECT value FROM cyphernode_props WHERE category='notifier' AND property='tg_base_url'")
+ returncode=$?
+ trace "[loadConfig] TG_BOT_URL [${TG_BOT_URL}]"
+ trace_rc ${returncode}
+
+ [ "${returncode}" -ne "0" ] && return 10
+
+ trace "[loadConfig] Looking up TG_API_KEY in database"
+ TG_API_KEY=$(sql "SELECT value FROM cyphernode_props WHERE category='notifier' AND property='tg_api_key'")
+ returncode=$?
+ trace "[loadConfig] TG_API_KEY [${TG_API_KEY}]"
+ trace_rc ${returncode}
+ [ "${returncode}" -ne "0" ] && return 20
+
+ trace "[loadConfig] Looking up TG_CHAT_ID in database"
+ TG_CHAT_ID=$(sql "SELECT value FROM cyphernode_props WHERE category='notifier' AND property='tg_chat_id'")
+ returncode=$?
+ trace "[loadConfig] TG_CHAT_ID [${TG_CHAT_ID}]"
+ trace_rc ${returncode}
+ [ "${returncode}" -ne "0" ] && return 30
+ else
+ trace "[loadConfig] FEATURE_TELEGRAM is DISABLED"
+ fi
}
main
diff --git a/notifier_docker/script/sql.sh b/notifier_docker/script/sql.sh
new file mode 100644
index 000000000..bee2e6dcf
--- /dev/null
+++ b/notifier_docker/script/sql.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+. ./trace.sh
+
+sql() {
+ trace "Entering sql()..."
+
+ local select_id=${2}
+ local response
+ local inserted_id
+
+ trace "[sql] psql -qAtX -h postgres -U cyphernode -c \"${1}\""
+ response=$(psql -qAtX -h postgres -U cyphernode -c "${1}")
+ returncode=$?
+ trace_rc ${returncode}
+
+ if [ -n "${select_id}" ]; then
+ if [ "${returncode}" -eq "0" ]; then
+ inserted_id=$(echo "${response}" | cut -d ' ' -f1)
+ else
+ trace "[sql] psql -qAtX -h postgres -U cyphernode -c \"${select_id}\""
+ inserted_id=$(psql -qAtX -h postgres -U cyphernode -c "${select_id}")
+ returncode=$?
+ trace_rc ${returncode}
+ fi
+ echo -n "${inserted_id}"
+ else
+ echo -n "${response}"
+ fi
+
+ return ${returncode}
+}
+
+waitfortable(){
+ TABLE_NAME=$1
+
+ trace "Entering waitfortable [$TABLE_NAME]"
+
+ while true; do
+
+ exists=$(psql -qAtX -h postgres -U cyphernode -c "SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname='public' and tablename='$TABLE_NAME')")
+
+ if [ "${exists}" = "t" ]; then
+ trace "Table found [$TABLE_NAME] - Exiting"
+ break
+ fi
+
+ trace "wainting for table [$TABLE_NAME] to exist"
+ sleep 5
+ done
+}
diff --git a/notifier_docker/script/start-tg-setup.sh b/notifier_docker/script/start-tg-setup.sh
new file mode 100755
index 000000000..674a2ff02
--- /dev/null
+++ b/notifier_docker/script/start-tg-setup.sh
@@ -0,0 +1,2 @@
+docker exec -it $(docker ps -q -f "name=cyphernode_notifier") ./tgsetup.sh
+
diff --git a/notifier_docker/script/startnotifier.sh b/notifier_docker/script/startnotifier.sh
index 4df3e153f..e5a5198cc 100644
--- a/notifier_docker/script/startnotifier.sh
+++ b/notifier_docker/script/startnotifier.sh
@@ -1,7 +1,21 @@
#!/bin/sh
-
. ./trace.sh
+wait_for_broker() {
+ trace "[startnotifier-wait_for_broker] Waiting for broker to be ready"
+
+ while true ; do ping -c 1 broker ; [ "$?" -eq "0" ] && break ; sleep 5; done
+}
+
trace "Starting mosquitto and subscribing to the notifier topic..."
+if [ "${FEATURE_TELEGRAM}" = "true" ]; then
+ trace "[startnotifier] Waiting for PostgreSQL to be ready..."
+ while [ ! -f "/container_monitor/postgres_ready" ]; do trace "[startnotifier] PostgreSQL not ready" ; sleep 10 ; done
+ trace "[startnotifier] PostgreSQL ready!"
+fi
+
+# Wait for broker to be ready
+wait_for_broker
+
exec sh -c 'mosquitto_sub -h broker -t notifier | ./requesthandler.sh'
diff --git a/notifier_docker/script/tgsetup.sh b/notifier_docker/script/tgsetup.sh
new file mode 100755
index 000000000..8ce594dae
--- /dev/null
+++ b/notifier_docker/script/tgsetup.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+. ./colors.sh
+
+# Cyphernode Telegram configuration
+#
+#
+sql() {
+ local select_id=${2}
+ local response
+ local inserted_id
+
+ response=$(psql -qAtX -h postgres -U cyphernode -c "${1}")
+ returncode=$?
+
+ if [ -n "${select_id}" ]; then
+ if [ "${returncode}" -eq "0" ]; then
+ inserted_id=$(echo -e "${response}" | cut -d ' ' -f1)
+ else
+ inserted_id=$(psql -qAtX -h postgres -U cyphernode -c "${select_id}")
+ returncode=$?
+ fi
+ fi
+
+ return ${returncode}
+}
+
+# Ping the database an make sure it's UP
+echo -e "\r\n$BIBlue"; echo -e "[TG Setup] Testing database before starting the configuration"
+
+ping -c 1 postgres 2>&1 > /dev/null
+rc=$?
+
+if [ $rc != 0 ]; then
+ echo -e "\r\n$BIRed"; echo -e "[TG Setup] Database is not up. Make sure Cyphernode is running before setting up Telegram. Exiting\r\n"
+ exit 1
+else
+ echo -e "\r\n$Green"; echo -e "[TG Setup] Database is alive"
+fi
+
+if [ -n "$TOR_TELEGRAM" ] && [ "$TOR_TELEGRAM" = "1" ]; then
+ echo -e "[TG Setup] Sending Telegram messages usging tor\r\n"
+else
+ echo -e "[TG Setup] Sending Telegram messages usging clearnet\r\n"
+fi
+
+while true; do
+ echo -e "\r\n$Green";
+ read -p "[TG Setup] Do you wish to configure Telegram for Cyphernode? [yn] " -n 1 -r
+
+ case $REPLY in
+ [Yy]* ) break;;
+ [Nn]* ) echo -e "\r\n[TG Setup] Got it! You can always come back later"; exit;;
+ * ) echo -e "[31mPlease answer yes or no.";;
+ esac
+done
+
+# Set the base Telegram URL in DB
+echo -e $Blue; echo -e "\r\n[TG Setup] Adding the Telegram base URL in database config table cyphernode_props\r\n"
+
+TG_BASE_URL="https://api.telegram.org/bot"
+sql "INSERT INTO cyphernode_props (category, property, value) VALUES ('notifier', 'tg_base_url', '$TG_BASE_URL') \
+ ON CONFLICT (category, property) DO NOTHING"
+
+echo -e ""
+echo -e "[31m[TG Setup] Please go into your Telegram App and start chatting with the @BotFather[0m\r\n"
+echo -e "\r\n$Blue";
+echo -e "==> (Step 1) Enter @Botfather in the search tab and choose this bot"
+echo -e "==> Note, official Telegram bots have a blue checkmark beside their name"
+echo -e "==> (Step 2) Click “Start” to activate BotFather bot. In response, you receive a list of commands to manage bots"
+echo -e "==> (Step 3) Choose or type the /newbot command and send it"
+echo -e "==> @BotFather replies: Alright, a new bot. How are we going to call it? Please choose a name for your bot"
+echo -e "==> (Step 4) Choose a name for your bot. And choose a username for your bot — the bot can be found by its username in searches. The username must be unique and end with the word 'bot'"
+echo -e "==> After you choose a suitable name for your bot — the bot is created. You will receive a message with a link to your bot t.me/"
+echo -e "==> Cyphernode needs the generated token to access the API: Copy the line below following the message 'Use this token to access the HTTP API' "
+echo -e "\r\n\r\n"
+
+while true; do
+ echo -e "\r\n$Green";
+
+ # 46 characters 1234567890:ABCrWd1mHlWzGM-2ovbxRnOF_g3V2-csY4E
+ # matching '^[0-9]{10}:.{35}$'
+ read -p "[TG Setup] Enter the token here: " -n 46 -r
+
+ if [[ ${#REPLY} -gt 0 ]] && [[ $REPLY =~ ^[0-9]{10}:.{35}$ ]]; then
+ # Token is good - continue
+ break
+ else
+ echo -e "$BIRed"
+ echo -e "[TG Setup] Oooops, it doesn't seem to be a valid token."
+ echo -e "[TG Setup] The token should be a string with this format 1234567890:ABCrWd1mHlWzGM-2ovbxRnOF_g3V2-csY4E."
+ echo -e "[TG Setup] Please enter the token again - 10 digits:35 characters"
+ fi
+done
+
+# Now let's ping Telegram while we ask the user to type a message in Telegram
+
+echo -e "\r\n$Green";
+echo -e "\r\n[TG Setup] Telegram Setup will now try to obtain the chat ID from the Telgram server.\r\n"
+
+echo -e "To make this happen, please go into the Telegram App and send a message to the new bot"
+echo -e "Click on the link in the @BotFather's answer : Congratulations on your new bot. You will find it at t.me/your-new-bot."
+
+#
+# The server will return something like below after the user sends a message
+# '{"ok":true,"result":[{"update_id":846048856,
+# "message":{"message_id":1,"from":{"id":6666666666,"is_bot":false,"first_name":"Phil","last_name":"","username":"phil","language_code":"en"},"chat":{"id":6666666666,"first_name":"Phil","last_name":"","username":"phil","type":"private"},"date":1649860823,"text":"/start","entities":[{"offset":0,"length":6,"type":"bot_command"}]}},{"update_id":666048857,
+# "message":{"message_id":2,"from":{"id":6666666666,"is_bot":false,"first_name":"Phil","last_name":"","username":"phil","language_code":"en"},"chat":{"id":6666666666,"first_name":"Phil","last_name":"","username":"phil","type":"private"},"date":1649860826,"text":"hello"}}]}'
+#
+
+while true; do
+ echo -e "Trying to contact Telegram server..."
+ httpStatusCode=$(curl -o /dev/null -s -w '%{http_code}' $TG_BASE_URL$REPLY/getUpdates)
+
+ if [[ $httpStatusCode == 200 ]]; then
+ TG_API_KEY=$REPLY
+ sql "INSERT INTO cyphernode_props (category, property, value) VALUES ('notifier', 'tg_api_key', '$TG_API_KEY') \
+ ON CONFLICT (category, property) DO UPDATE SET value='$TG_API_KEY'"
+
+ loop=1
+ while [ $loop -le 10 ]; do
+ response=$(curl -s $TG_BASE_URL$TG_API_KEY/getUpdates)
+ isOk=$(echo -e $response | jq '.ok')
+ if [ "$isOk" = "true" ]; then
+ # get the chat id from the last message
+ TG_CHAT_ID=$(echo -e $response | jq '.result[-1].message.chat.id')
+
+ if [[ -z $TG_CHAT_ID || "$TG_CHAT_ID" == "null" ]]; then
+ echo -e $Yellow; echo -e "[$loop] [TG Setup] Received positive answer from Telegram without a chat id - Waiting for$IRed YOU$Yellow to send a message in the chat..."
+ sleep 10
+ loop=$(( $loop + 1 ))
+ else
+ # Save the TG_CHAT_ID
+ today=`date -u`
+ sql "INSERT INTO cyphernode_props (category, property, value) VALUES ('notifier', 'tg_chat_id', '$TG_CHAT_ID') \
+ ON CONFLICT (category, property) DO UPDATE SET value=$TG_CHAT_ID"
+
+ echo -e "$Green"
+ echo -e "[TG Setup] Reloading configs\r\n"
+
+ response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"reloadConfig\"}")
+
+ echo -e ""
+ echo -e "[TG Setup] Sending message to Telegram [$today]"
+
+ if [ "${TOR_TELEGRAM}" = "true" ]; then
+ body=$(echo -e "{\"text\":\"Hello from Cyphernode [$today] using tor - setup is complete\"}" | base64 | tr -d '\n')
+ response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramGroup\",\"body\":\"${body}\",\"tor\":true}")
+ else
+ body=$(echo -e "{\"text\":\"Hello from Cyphernode [$today] using clearnet - setup is complete\"}" | base64 | tr -d '\n')
+ response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramGroup\",\"body\":\"${body}\"}")
+ fi
+
+ echo -e "$BBlue"
+ echo -e "\r\n[TG Setup] Ok. Done."
+ exit
+ fi
+ else
+ echo -e "\r\n[31m[TG Setup] Server returned an error [$response] - exiting[0m"; exit
+ fi
+ done
+ echo -e "\r\n[31m[TG Setup] No message found. Please go into the Telegram App and send a message to the new bot - exiting[0m"; exit
+ else
+ echo -e "\r\n[31m[TG Setup] Server returned a HTTP error code [$httpStatusCode] - exiting[0m"; break
+ fi
+done
diff --git a/notifier_docker/script/web.sh b/notifier_docker/script/web.sh
index 390dbb9c5..b7ad0b9fd 100644
--- a/notifier_docker/script/web.sh
+++ b/notifier_docker/script/web.sh
@@ -68,7 +68,7 @@ curl_it() {
rc=$(curl ${tor} -o webresponse-${rnd} -m 20 -w "%{http_code}" -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d "${data}" -k ${url})
returncode=$?
else
- trace "[curl_it] curl ${tor} -o webresponse-$$ -m 20 -w \"%{http_code}\" -k ${url}"
+ trace "[curl_it] curl ${tor} -o webresponse-${rnd} -m 20 -w \"%{http_code}\" -k ${url}"
rc=$(curl ${tor} -o webresponse-${rnd} -m 20 -w "%{http_code}" -k ${url})
returncode=$?
fi
@@ -76,10 +76,13 @@ curl_it() {
trace_rc ${returncode}
if [ "${returncode}" -eq "0" ]; then
- response=$(cat webresponse-${rnd} | base64 | tr -d '\n' ; rm webresponse-${rnd})
+ response=$(cat webresponse-${rnd} | base64 | tr -d '\n')
else
response=
fi
+
+ rm webresponse-${rnd}
+
# When curl is unable to connect, http_code is "000" which is not a valid JSON number
[ "${rc}" -eq "0" ] && rc=0
response="{\"curl_code\":${returncode},\"http_code\":${rc},\"body\":\"${response}\"}"
diff --git a/proxy_docker/app/script/notify.sh b/proxy_docker/app/script/notify.sh
index 42d9fa7b5..8148e2bf6 100644
--- a/proxy_docker/app/script/notify.sh
+++ b/proxy_docker/app/script/notify.sh
@@ -49,4 +49,53 @@ notify_web() {
return ${curl_code} || ${returncode}
fi
+}
+
+#
+# call notify_telegram "text to send". See https://core.telegram.org/bots/api#sendmessage
+# ex in shell script: notify_telegram "Unit testing notify_telegram at `date -u +"%FT%H%MZ"`"
+#
+notify_telegram() {
+ trace "Entering notify_telegram()..."
+
+ local body=$(echo {\"text\":\"$1\"} | base64 | tr -d '\n')
+
+ local returncode
+ local response
+ local http_code
+ local curl_code
+ local msg
+
+ if [ "$TOR_TELEGRAM" = "true" ]; then
+ msg="{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramGroup\",\"body\":\"${body}\",\"tor\":true}"
+ else
+ msg="{\"response-topic\":\"response/$$\",\"cmd\":\"sendToTelegramGroup\",\"body\":\"${body}\"}"
+ fi
+
+ # We use the pid as the response-topic, so there's no conflict in responses.
+ trace "[notify_telegram] mosquitto_rr -h broker -W 21 -t notifier -e \"response/$$\" -m \"${msg}\""
+ response=$(mosquitto_rr -h broker -W 21 -t notifier -e "response/$$" -m ${msg})
+ returncode=$?
+ trace_rc ${returncode}
+
+ # The response looks like this: {"curl_code":0,"http_code":200,"body":"..."} where the body
+ # is the base64(response body) but we don't need the response content here.
+ trace "[notify_telegram] response=${response}"
+ curl_code=$(echo "${response}" | jq -r ".curl_code")
+ trace "[notify_telegram] curl_code=${curl_code}"
+ http_code=$(echo "${response}" | jq -r ".http_code")
+ trace "[notify_telegram] http_code=${http_code}"
+
+ echo "${response}"
+
+ if [ "${curl_code}" -eq "0" ] && [ "${returncode}" -eq "0" ]; then
+ if [ "${http_code}" -lt "400" ]; then
+ return 0
+ else
+ return ${http_code}
+ fi
+ else
+ return ${curl_code} || ${returncode}
+ fi
+
}
\ No newline at end of file
diff --git a/proxy_docker/app/script/requesthandler.sh b/proxy_docker/app/script/requesthandler.sh
index e6078d16e..5d89021ec 100644
--- a/proxy_docker/app/script/requesthandler.sh
+++ b/proxy_docker/app/script/requesthandler.sh
@@ -1,8 +1,5 @@
#!/bin/sh
#
-#
-#
-#
. ./db/config.sh
. ./sendtobitcoinnode.sh
diff --git a/proxy_docker/app/tests/test-telegram.sh b/proxy_docker/app/tests/test-telegram.sh
new file mode 100755
index 000000000..6405a0e0f
--- /dev/null
+++ b/proxy_docker/app/tests/test-telegram.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# Tests the notify_telegram (in notify.sh) function by calling it directly - not going through the proxy, there is no endpoint.
+
+cd ..
+. ./notify.sh
+
+echo "Calling notify_telegram..."
+
+notify_telegram "Unit testing notify_telegram at `date -u +"%FT%H%MZ"`"
+
+echo "Done..."
+