2016-01-02 19:35:15 +00:00
#!/bin/bash
2020-12-27 13:53:00 +00:00
##################################################################
#
# File: bashbot.sh
# Note: DO NOT EDIT! this file will be overwritten on update
2021-01-01 20:36:48 +00:00
# shellcheck disable=SC2140,SC2031,SC2120,SC1091,SC1117,SC2059
#
# Description: bashbot, the Telegram bot written in bash.
2019-03-28 15:51:33 +00:00
#
2020-12-27 13:53:00 +00:00
# Written by Drew (@topkecleon) KayM (@gnadelwartz).
2021-01-02 09:18:58 +00:00
# Also contributed: Daniil Gentili (@danog), JuanPotato, BigNerd95,
# TiagoDanin, iicc1, dcoomber
2020-12-27 13:53:00 +00:00
# https://github.com/topkecleon/telegram-bot-bash
#
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
2019-04-01 10:52:25 +00:00
#
2021-01-15 18:05:53 +00:00
# Usage: bashbot.sh BOTCOMMAND
2021-01-09 06:46:46 +00:00
BOTCOMMANDS = "-h help init start stop status suspendback resumeback killback"
2021-01-01 20:36:48 +00:00
#
2019-04-01 10:52:25 +00:00
# Exit Codes:
2020-12-27 13:53:00 +00:00
# 0 - success (hopefully)
# 1 - can't change to dir
# 2 - can't write to tmp, count or token
# 3 - user / command / file not found
# 4 - unknown command
2021-01-01 18:56:19 +00:00
# 5 - cannot start, stop or get status
2020-12-27 13:53:00 +00:00
# 6 - mandatory module not found
# 7 - can't get bottoken
# 8 - curl/wget missing
# 10 - not bash!
#
2021-09-20 18:32:51 +00:00
#### $$VERSION$$ v1.52-dev-1-g5205fe3
2020-12-27 13:53:00 +00:00
##################################################################
2019-03-22 16:47:36 +00:00
2020-06-23 14:35:50 +00:00
# are we running in a terminal?
2020-12-26 20:18:18 +00:00
NN = "\n"
2021-01-04 22:08:09 +00:00
if [ -t 1 ] && [ -n " ${ TERM } " ] ; then
2020-12-27 10:26:38 +00:00
INTERACTIVE = 'yes'
2019-03-28 13:24:08 +00:00
RED = '\e[31m'
GREEN = '\e[32m'
ORANGE = '\e[35m'
2020-05-18 12:57:53 +00:00
GREY = '\e[1;30m'
2019-03-28 13:24:08 +00:00
NC = '\e[0m'
2020-12-27 11:38:52 +00:00
NN = " ${ NC } \n "
2019-03-28 13:24:08 +00:00
fi
2021-02-01 08:37:22 +00:00
declare -r INTERACTIVE RED GREEN ORANGE GREY NC NN
2019-03-28 13:24:08 +00:00
2020-12-24 08:19:54 +00:00
# telegram uses utf-8 characters, check if we have an utf-8 charset
if [ " ${ LANG } " = " ${ LANG %[Uu][Tt][Ff]* } " ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } Warning: Telegram uses utf-8, but looks like you are using non utf-8 locale: ${ NC } ${ LANG } \n "
2020-12-24 08:19:54 +00:00
fi
2020-09-27 18:24:15 +00:00
# we need some bash 4+ features, check for old bash by feature
2021-01-01 16:14:59 +00:00
if [ " $( { LC_ALL = C.utf-8 printf "%b" "\u1111" ; } 2>/dev/null) " = "\u1111" ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } Warning: Missing unicode '\uxxxx' support, missing C.utf-8 locale or to old bash version. ${ NN } "
2020-09-27 18:24:15 +00:00
fi
2020-12-07 14:50:56 +00:00
2021-01-04 15:03:53 +00:00
# in UTF-8 äöü etc. are part of [:alnum:] and ranges (e.g. a-z), but we want ASCII a-z ranges!
# for more information see doc/4_expert.md#Character_classes
azazaz = 'abcdefghijklmnopqrstuvwxyz' # a-z :lower:
AZAZAZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # A-Z :upper:
2021-01-04 15:10:43 +00:00
o9o9o9 = '0123456789' # 0-9 :digit:
2021-01-04 15:03:53 +00:00
azAZaz = " ${ azazaz } ${ AZAZAZ } " # a-zA-Z :alpha:
2021-01-04 15:10:43 +00:00
azAZo9 = " ${ azAZaz } ${ o9o9o9 } " # a-zA-z0-9 :alnum:
2021-01-04 15:03:53 +00:00
2020-05-14 19:31:52 +00:00
# some important helper functions
# returns true if command exist
2020-06-10 09:11:42 +00:00
_exists( ) {
2021-01-05 15:17:34 +00:00
[ " $( type -t " $1 " ) " = "file" ]
2020-05-14 19:31:52 +00:00
}
# execute function if exists
_exec_if_function( ) {
2021-01-05 15:17:34 +00:00
[ " $( type -t " $1 " ) " != "function" ] && return 1
2020-06-15 07:09:08 +00:00
" $@ "
2020-05-14 19:31:52 +00:00
}
# returns true if function exist
2020-06-10 09:11:42 +00:00
_is_function( ) {
2021-01-05 15:17:34 +00:00
[ " $( type -t " $1 " ) " = "function" ]
2020-06-10 09:11:42 +00:00
}
# round $1 in international notation! , returns float with $2 decimal digits
2020-08-01 09:37:32 +00:00
# if $2 is not given or is not a positive number zero is assumed
2020-06-10 09:11:42 +00:00
_round_float( ) {
2021-01-05 15:17:34 +00:00
local digit = " $2 " ; [ [ " $2 " = ~ ^[ ${ o9o9o9 } ] +$ ] ] || digit = "0"
2021-01-18 20:09:30 +00:00
: " $( LC_ALL = C printf " %. ${ digit } f " " $1 " 2>/dev/null) "
2021-01-20 18:50:19 +00:00
printf "%s" " ${ _ //,/. } " # make more LANG independent
2020-05-14 19:31:52 +00:00
}
2021-01-06 07:48:25 +00:00
# date is external, printf is much faster
_date( ) {
printf "%(%c)T\n" -1
}
2020-06-13 19:39:52 +00:00
setConfigKey( ) {
2021-01-04 15:10:43 +00:00
[ [ " $1 " = ~ ^[ -${ azAZo9 } ,._] +$ ] ] || return 3
2020-06-20 18:12:36 +00:00
[ -z " ${ BOTCONFIG } " ] && return 1
2020-06-19 10:49:18 +00:00
printf '["%s"]\t"%s"\n' " ${ 1 //,/ \" , \" } " " ${ 2 // \" / \\ \" } " >>" ${ BOTCONFIG } .jssh "
2020-06-01 07:54:35 +00:00
}
2020-06-12 19:18:32 +00:00
getConfigKey( ) {
2021-01-04 15:10:43 +00:00
[ [ " $1 " = ~ ^[ -${ azAZo9 } ,._] +$ ] ] || return 3
2021-01-16 10:20:29 +00:00
[ -r " ${ BOTCONFIG } .jssh " ] && sed -n 's/\["' " $1 " '"\]\t*"\(.*\)"/\1/p' " ${ BOTCONFIG } .jssh " | tail -n 1
2020-06-01 07:54:35 +00:00
}
2021-02-05 13:25:21 +00:00
# escape characters in json strings for telegram
# $1 string, output escaped string
2021-01-23 13:06:55 +00:00
JsonEscape( ) {
2021-02-05 13:46:11 +00:00
sed -E -e 's/\r//g' -e 's/([-"`´ ,§$%&/(){}#@!?*.\t])/\\\1/g' <<< " ${ 1 // $'\n' / \\ n } "
2021-01-23 13:06:55 +00:00
}
2021-02-05 13:25:21 +00:00
# clean \ from escaped json string
# $1 string, output cleaned string
2021-02-21 22:34:34 +00:00
cleanEscape( ) { # remove " all \ but \n\u \n or \r
2021-02-10 20:30:42 +00:00
sed -E -e 's/\\"/+/g' -e 's/\\([^nu])/\1/g' -e 's/(\r|\n)//g' <<< " $1 "
2021-02-05 13:25:21 +00:00
}
2020-08-01 09:37:32 +00:00
# check if $1 seems a valid token
2020-06-27 08:43:08 +00:00
# return true if token seems to be valid
2020-06-27 08:29:36 +00:00
check_token( ) {
2021-01-05 15:17:34 +00:00
[ [ " $1 " = ~ ^[ ${ o9o9o9 } ] { 8,10} :[ ${ azAZo9 } _-] { 35} $ ] ] && return 0
2020-06-27 08:29:36 +00:00
return 1
}
2020-12-30 09:03:23 +00:00
# log $1 with date
2021-01-06 14:33:07 +00:00
log_error( ) { printf "%(%c)T: %s\n" -1 " $* " >>" ${ ERRORLOG } " ; }
log_debug( ) { printf "%(%c)T: %s\n" -1 " $* " >>" ${ DEBUGLOG } " ; }
log_update( ) { printf "%(%c)T: %s\n" -1 " $* " >>" ${ UPDATELOG } " ; }
2020-12-30 09:07:10 +00:00
# log $1 with date, special first \n
2021-01-06 14:33:07 +00:00
log_message( ) { printf "\n%(%c)T: %s\n" -1 " ${ 1 / \\ n / $'\n' } " >>" ${ MESSAGELOG } " ; }
2021-01-14 21:15:32 +00:00
# curl is preferred, try detect curl even not in PATH
# sets BASHBOT_CURL to point to curl
DETECTED_CURL = "curl"
detect_curl( ) {
local file warn = "Warning: Curl not detected, try fallback to wget! pls install curl or adjust BASHBOT_CURL/BASHBOT_WGET environment variables."
# custom curl command
[ -n " ${ BASHBOT_CURL } " ] && return 0
# use wget
[ -n " ${ BASHBOT_WGET } " ] && DETECTED_CURL = "wget" && return 1
# default use curl in PATH
BASHBOT_CURL = "curl"
_exists curl && return 0
# search in usual locations
for file in /usr/bin /bin /usr/local/bin; do
[ -x " ${ file } /curl " ] && BASHBOT_CURL = " ${ file } /curl " && return 0
done
# curl not in PATH and not in usual locations
DETECTED_CURL = "wget"
log_update " ${ warn } " ; [ -n " ${ BASHBOTDEBUG } " ] && log_debug " ${ warn } "
return 1
}
2020-12-27 13:53:00 +00:00
2020-06-27 08:29:36 +00:00
# additional tests if we run in debug mode
2020-06-30 05:26:16 +00:00
export BASHBOTDEBUG
2020-07-10 06:39:33 +00:00
[ [ " ${ BASH_ARGV [0] } " = = *"debug" * ] ] && BASHBOTDEBUG = "yes"
2020-12-27 13:53:00 +00:00
2020-07-10 06:39:33 +00:00
# $1 where $2 command $3 may debug
2020-07-11 06:34:57 +00:00
# shellcheck disable=SC2094
2020-12-15 16:19:09 +00:00
debug_checks( ) { {
2020-07-09 20:31:58 +00:00
[ -z " ${ BASHBOTDEBUG } " ] && return
2021-01-15 12:09:32 +00:00
local token where = " $1 " ; shift
2021-01-06 14:33:07 +00:00
printf "%(%c)T: debug_checks: %s: bashbot.sh %s\n" -1 " ${ where } " " ${ 1 ##*/ } "
2020-07-11 06:34:57 +00:00
# shellcheck disable=SC2094
2021-01-06 14:33:07 +00:00
[ -z " ${ DEBUGLOG } " ] && printf "%(%c)T: %s\n" -1 "DEBUGLOG not set! =========="
2021-01-06 07:48:25 +00:00
token = " $( getConfigKey "bottoken" ) "
2021-01-06 14:33:07 +00:00
[ -z " ${ token } " ] && printf "%(%c)T: %s\n" -1 "Bot token is missing! =========="
check_token " ${ token } " || printf "%(%c)T: %s\n%s\n" -1 "Invalid bot token! ==========" " ${ token } "
[ -z " $( getConfigKey "botadmin" ) " ] && printf "%(%c)T: %s\n" -1 "Bot admin is missing! =========="
2020-07-02 15:24:56 +00:00
# call user defined debug_checks if exists
2021-01-06 07:48:25 +00:00
_exec_if_function my_debug_checks " $( _date) " " ${ where } " " $* "
2021-02-07 15:56:35 +00:00
} 2>/dev/null >>" ${ DEBUGLOG } "
2020-12-15 16:19:09 +00:00
}
2020-05-14 19:31:52 +00:00
2021-01-02 05:17:02 +00:00
# some Linux distributions (e.g. Manjaro) doesn't seem to have C locale activated by default
2021-01-03 21:39:36 +00:00
if _exists locale && [ " $( locale -a | grep -c -e " ^C $" -e "^C.[uU][tT][fF]" ) " -lt 2 ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } Warning: locale ${ NC } ${ GREY } C ${ NC } ${ ORANGE } and/or ${ NC } ${ GREY } C.utf8 ${ NC } ${ ORANGE } seems missing, use \" ${ NC } ${ GREY } locale -a ${ NC } ${ ORANGE } \" to show what locales are installed on your system. ${ NN } "
2020-12-07 14:50:56 +00:00
fi
2019-04-23 11:07:20 +00:00
# get location and name of bashbot.sh
SCRIPT = " $0 "
2019-05-23 17:40:15 +00:00
REALME = " ${ BASH_SOURCE [0] } "
SCRIPTDIR = " $( dirname " ${ REALME } " ) "
RUNDIR = " $( dirname " $0 " ) "
MODULEDIR = " ${ SCRIPTDIR } /modules "
2021-02-01 09:00:08 +00:00
# adjust stuff for source, use return from source without source
2021-02-07 10:02:01 +00:00
exit_source( ) { exit " $1 " ; }
2021-02-01 09:00:08 +00:00
if [ [ " ${ SCRIPT } " != " ${ REALME } " || " $1 " = = "source" ] ] ; then
SOURCE = "yes"
2021-06-03 11:00:37 +00:00
SCRIPT = " ${ REALME } "
2021-02-07 10:02:01 +00:00
[ -z " $1 " ] && exit_source( ) { printf "Exit from source ...\n" ; return " $1 " ; }
2021-02-01 09:00:08 +00:00
fi
# emmbeded system may claim bash but it is not
# check for bash like ARRAY handlung
if ! ( unset a; set -A a a; eval "a=(a b)" ; eval '[ -n "${a[1]}" ]' ; ) > /dev/null 2>& 1; then
printf "Error: Current shell does not support ARRAY's, may be busybox ash shell. pls install a real bash!\n"
exit_source 10
fi
2019-05-23 10:26:53 +00:00
2021-02-01 09:00:08 +00:00
# adjust path variables
2021-01-04 22:08:09 +00:00
if [ -n " ${ BASHBOT_HOME } " ] ; then
SCRIPTDIR = " ${ BASHBOT_HOME } "
2020-06-04 14:10:58 +00:00
else
BASHBOT_HOME = " ${ SCRIPTDIR } "
2019-05-23 10:26:53 +00:00
fi
2021-01-04 22:08:09 +00:00
[ -z " ${ BASHBOT_ETC } " ] && BASHBOT_ETC = " ${ BASHBOT_HOME } "
[ -z " ${ BASHBOT_VAR } " ] && BASHBOT_VAR = " ${ BASHBOT_HOME } "
2019-05-23 10:26:53 +00:00
2020-06-06 07:21:56 +00:00
ADDONDIR = " ${ BASHBOT_ETC :- . } /addons "
2021-02-01 09:00:08 +00:00
RUNUSER = " ${ USER } " # save original USER
2019-03-31 10:52:11 +00:00
2021-01-01 20:36:48 +00:00
# provide help
case " $1 " in
2021-01-08 21:32:05 +00:00
"" ) [ -z " ${ SOURCE } " ] && printf " ${ ORANGE } Available commands: ${ GREY } ${ BOTCOMMANDS } ${ NN } " && exit
2021-01-01 23:40:42 +00:00
; ;
2021-01-09 21:25:15 +00:00
"-h" *) LOGO = " ${ BASHBOT_HOME :- . } /doc/bashbot.ascii "
2021-01-15 18:05:53 +00:00
{ [ -r " ${ LOGO } " ] && cat " ${ LOGO } "
2021-01-16 10:20:29 +00:00
sed -nE -e '/(NOT EDIT)|(shellcheck)/d' -e '3,/###/p' " $0 " ; } | more
2021-01-01 20:36:48 +00:00
exit; ;
"help" ) HELP = " ${ BASHBOT_HOME :- . } /README "
if [ -n " ${ INTERACTIVE } " ] ; then
2021-01-04 22:08:09 +00:00
_exists w3m && w3m " ${ HELP } .html " && exit
_exists lynx && lynx " ${ HELP } .html " && exit
_exists less && less " ${ HELP } .txt " && exit
2021-01-01 20:36:48 +00:00
fi
2021-01-04 22:08:09 +00:00
cat " ${ HELP } .txt "
2021-01-01 20:36:48 +00:00
exit; ;
esac
2020-12-27 13:53:00 +00:00
# OK, ENVIRONMENT is set up, let's do some additional tests
2021-01-04 22:08:09 +00:00
if [ [ -z " ${ SOURCE } " && -z " ${ BASHBOT_HOME } " ] ] && ! cd " ${ RUNDIR } " ; then
2020-12-26 20:18:18 +00:00
printf " ${ RED } ERROR: Can't change to ${ RUNDIR } ... ${ NN } "
2021-02-01 09:00:08 +00:00
exit_source 1
2019-03-22 16:47:36 +00:00
fi
2021-01-01 23:11:23 +00:00
RUNDIR = "."
[ ! -w "." ] && printf " ${ ORANGE } WARNING: ${ RUNDIR } is not writeable! ${ NN } "
2019-03-18 13:45:19 +00:00
2020-12-27 13:53:00 +00:00
# check if JSON.sh is available
JSONSHFILE = " ${ BASHBOT_JSONSH :- ${ SCRIPTDIR } /JSON.sh/JSON.sh } "
2021-02-01 08:37:22 +00:00
if [ ! -x " ${ JSONSHFILE } " ] ; then
printf " ${ RED } ERROR: ${ NC } ${ JSONSHFILE } ${ RED } does not exist, are we in dev environment? ${ NN } ${ GREY } %s ${ NN } \n " \
"\$JSONSHFILE is set wrong or bashbot is not installed correctly, see doc/0_install.md"
exit_source 3
fi
2020-12-27 13:53:00 +00:00
# file locations based on ENVIRONMENT
2020-06-19 10:49:18 +00:00
BOTCONFIG = " ${ BASHBOT_ETC :- . } /botconfig "
2020-05-14 18:33:30 +00:00
BOTACL = " ${ BASHBOT_ETC :- . } /botacl "
DATADIR = " ${ BASHBOT_VAR :- . } /data-bot-bash "
2020-05-29 18:11:51 +00:00
BLOCKEDFILE = " ${ BASHBOT_VAR :- . } /blocked "
2020-05-14 18:33:30 +00:00
COUNTFILE = " ${ BASHBOT_VAR :- . } /count "
2020-06-06 12:48:01 +00:00
LOGDIR = " ${ RUNDIR :- . } /logs "
2020-12-27 13:53:00 +00:00
# CREATE botconfig if not exist
2020-08-01 09:37:32 +00:00
# assume everything already set up correctly if TOKEN is set
2021-01-01 18:01:26 +00:00
if [ -z " ${ BOTTOKEN } " ] ; then
2020-06-19 10:54:05 +00:00
# BOTCONFIG does not exist, create
2021-01-01 18:01:26 +00:00
[ ! -f " ${ BOTCONFIG } .jssh " ] && printf '["bot_config_key"]\t"config_key_value"\n' >>" ${ BOTCONFIG } .jssh "
if [ -z " $( getConfigKey "bottoken" ) " ] ; then
# ask user for bot token
if [ -z " ${ INTERACTIVE } " ] && [ " $1 " != "init" ] ; then
2020-12-26 20:18:18 +00:00
printf " Running headless, set BOTTOKEN or run ${ SCRIPT } init first!\n "
2019-04-12 09:27:20 +00:00
exit 2
2021-01-01 18:01:26 +00:00
else
2021-01-01 20:48:59 +00:00
printf " ${ RED } ENTER BOT TOKEN... ${ NN } ${ ORANGE } PLEASE WRITE YOUR TOKEN HERE OR PRESS CTRL+C TO ABORT ${ NN } "
2020-06-13 19:39:52 +00:00
read -r token
2020-12-27 11:51:19 +00:00
printf "\n"
2021-01-01 18:01:26 +00:00
fi
[ -n " ${ token } " ] && printf '["bottoken"]\t"%s"\n' " ${ token } " >> " ${ BOTCONFIG } .jssh "
2020-05-14 18:33:30 +00:00
fi
2020-09-26 17:52:06 +00:00
# no botadmin, setup botadmin
2020-06-13 19:39:52 +00:00
if [ -z " $( getConfigKey "botadmin" ) " ] ; then
2020-12-27 13:58:34 +00:00
# ask user for bot admin
if [ -z " ${ INTERACTIVE } " ] ; then
2020-12-26 20:18:18 +00:00
printf "Running headless, set botadmin to AUTO MODE!\n"
2020-05-14 18:33:30 +00:00
else
2021-01-01 20:48:59 +00:00
printf " ${ RED } ENTER BOT ADMIN... ${ NN } ${ ORANGE } PLEASE WRITE YOUR TELEGRAM ID HERE OR PRESS ENTER\nTO MAKE FIRST USER TYPING '/start' BOT ADMIN ${ NN } ?\b "
2019-05-20 08:50:51 +00:00
read -r admin
2020-05-14 18:33:30 +00:00
fi
2020-06-13 19:39:52 +00:00
[ -z " ${ admin } " ] && admin = '?'
2020-06-19 10:49:18 +00:00
printf '["botadmin"]\t"%s"\n' " ${ admin } " >> " ${ BOTCONFIG } .jssh "
2020-05-14 18:33:30 +00:00
fi
2021-02-07 15:56:35 +00:00
2021-02-07 17:01:34 +00:00
# setup botacl file
if [ ! -f " ${ BOTACL } " ] ; then
printf " ${ GREY } Create initial ${ BOTACL } file. ${ NN } "
printf '\n' >" ${ BOTACL } "
fi
# check data dir file
if [ ! -w " ${ DATADIR } " ] ; then
printf " ${ RED } ERROR: ${ DATADIR } does not exist or is not writeable!. ${ NN } "
[ " $1 " != "init" ] && exit_source 2 # skip on init
fi
# setup count file
if [ ! -f " ${ COUNTFILE } .jssh " ] ; then
printf '["counted_user_chat_id"]\t"num_messages_seen"\n' >> " ${ COUNTFILE } .jssh "
elif [ ! -w " ${ COUNTFILE } .jssh " ] ; then
printf " ${ RED } WARNING: Can't write to ${ COUNTFILE } !. ${ NN } "
ls -l " ${ COUNTFILE } .jssh "
fi
# setup blocked file
if [ ! -f " ${ BLOCKEDFILE } .jssh " ] ; then
printf '["blocked_user_or_chat_id"]\t"name and reason"\n' >>" ${ BLOCKEDFILE } .jssh "
2020-05-29 18:11:51 +00:00
fi
2019-03-22 16:47:36 +00:00
fi
2020-05-29 18:11:51 +00:00
2020-12-15 16:19:09 +00:00
if [ [ ! -d " ${ LOGDIR } " || ! -w " ${ LOGDIR } " ] ] ; then
2020-07-09 11:41:09 +00:00
LOGDIR = " ${ RUNDIR :- . } "
fi
DEBUGLOG = " ${ LOGDIR } /DEBUG.log "
ERRORLOG = " ${ LOGDIR } /ERROR.log "
UPDATELOG = " ${ LOGDIR } /BASHBOT.log "
2020-12-15 09:12:18 +00:00
MESSAGELOG = " ${ LOGDIR } /MESSAGE.log "
2020-07-09 11:41:09 +00:00
2020-06-12 19:18:32 +00:00
# read BOTTOKEN from bot database if not set
2020-06-19 10:54:05 +00:00
if [ -z " ${ BOTTOKEN } " ] ; then
BOTTOKEN = " $( getConfigKey "bottoken" ) "
2020-06-19 11:15:50 +00:00
if [ -z " ${ BOTTOKEN } " ] ; then
2020-12-18 14:24:21 +00:00
BOTERROR = "Warning: can't get bot token, try to recover working config..."
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } ${ BOTERROR } ${ NC } "
2020-09-26 17:52:06 +00:00
if [ -r " ${ BOTCONFIG } .jssh.ok " ] ; then
log_error " ${ BOTERROR } "
2020-12-27 12:12:38 +00:00
mv " ${ BOTCONFIG } .jssh " " ${ BOTCONFIG } .jssh.bad "
2020-12-26 20:18:18 +00:00
cp " ${ BOTCONFIG } .jssh.ok " " ${ BOTCONFIG } .jssh " ; printf "OK\n"
2020-09-26 17:52:06 +00:00
BOTTOKEN = " $( getConfigKey "bottoken" ) "
else
2020-12-27 12:12:38 +00:00
printf " \n ${ RED } Error: Can't recover from missing bot token! Remove ${ BOTCONFIG } .jssh and run ${ NC } bashbot.sh init\n "
2021-02-01 09:00:08 +00:00
exit_source 7
2020-09-26 17:52:06 +00:00
fi
2020-06-19 10:54:05 +00:00
fi
2020-05-18 12:57:53 +00:00
fi
2020-06-19 10:54:05 +00:00
2020-05-19 12:58:29 +00:00
# BOTTOKEN format checks
2020-06-27 10:43:46 +00:00
if ! check_token " ${ BOTTOKEN } " ; then
2021-01-01 20:55:13 +00:00
printf " \n ${ ORANGE } Warning: Your bot token is incorrect, it should have the following format: ${ NC } \n%b%b " \
" <your_bot_id> ${ RED } : ${ NC } <35_alphanumeric_characters-hash> ${ RED } e.g. => ${ NC } 123456789 ${ RED } : ${ NC } Aa-Zz_0Aa-Zz_1Aa-Zz_2Aa-Zz_3Aa-Zz_4\n\n " \
" ${ GREY } Your bot token: ' ${ NC } ${ BOTTOKEN // : / ${ RED } : ${ NC } } '\n "
2020-12-27 11:34:39 +00:00
2021-01-04 15:10:43 +00:00
if [ [ ! " ${ BOTTOKEN } " = ~ ^[ ${ o9o9o9 } ] { 8,10} : ] ] ; then
2021-01-01 20:55:13 +00:00
printf " ${ GREY } \tHint: Bot id not a number or wrong len: ${ NC } $(( $( wc -c <<< " ${ BOTTOKEN % : * } " ) - 1 )) ${ GREY } but should be ${ NC } 8-10\n "
2020-12-27 11:34:39 +00:00
[ -n " $( getConfigKey "botid" ) " ] && printf " \t ${ GREEN } Did you mean: \" ${ NC } $( getConfigKey "botid" ) ${ GREEN } \" ? ${ NN } "
2020-12-05 13:11:21 +00:00
fi
2021-01-04 15:10:43 +00:00
[ [ ! " ${ BOTTOKEN } " = ~ :[ ${ azAZo9 } _-] { 35} $ ] ] && \
2021-01-01 20:55:13 +00:00
printf " ${ GREY } \tHint: Hash contains invalid character or has not len ${ NC } 35 ${ GREY } , hash len is ${ NC } $(( $( wc -c <<< " ${ BOTTOKEN #* : } " ) - 1 )) \n "
2020-12-27 11:34:39 +00:00
printf "\n"
2020-05-18 12:57:53 +00:00
fi
2019-03-22 16:47:36 +00:00
2020-05-20 14:38:56 +00:00
2020-05-14 18:33:30 +00:00
##################
# here we start with the real stuff
2021-01-20 18:50:19 +00:00
BASHBOT_RETRY = "" # retry by default
2019-04-12 18:49:58 +00:00
2020-06-10 06:20:48 +00:00
URL = " ${ BASHBOT_URL :- https : //api.telegram.org/bot } ${ BOTTOKEN } "
2021-02-06 15:05:18 +00:00
FILEURL = " ${ URL %%/bot* } /file/bot ${ BOTTOKEN } "
2021-01-04 22:08:09 +00:00
ME_URL = ${ URL } '/getMe'
2019-04-12 18:49:58 +00:00
2020-06-12 19:18:32 +00:00
#################
# BASHBOT COMMON functions
2020-12-31 17:02:31 +00:00
declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR BOTACL DATADIR COUNTFILE
2021-01-14 16:47:06 +00:00
declare -rx BOTTOKEN URL ME_URL
2019-05-28 19:12:02 +00:00
2019-05-28 18:50:19 +00:00
declare -ax CMD
2021-01-24 09:28:04 +00:00
declare -Ax UPD BOTSENT USER MESSAGE URLS CONTACT LOCATION CHAT FORWARD REPLYTO VENUE iQUERY iBUTTON
2020-12-13 11:00:18 +00:00
declare -Ax SERVICE NEWMEMBER LEFTMEMBER PINNED MIGRATE
2021-03-03 12:27:07 +00:00
export res CAPTION ME BOTADMIN
2019-04-12 18:49:58 +00:00
2019-05-26 19:25:01 +00:00
2021-01-20 17:20:03 +00:00
###############
# load modules
for module in " ${ MODULEDIR :- . } " /*.sh ; do
# shellcheck source=./modules/aliases.sh
if ! _is_function " $( basename " ${ module } " ) " && [ -r " ${ module } " ] ; then source " ${ module } " "source" ; fi
done
2020-06-08 10:58:36 +00:00
##################
2020-09-26 17:52:06 +00:00
# read commands file if we are not sourced
2019-05-10 09:33:41 +00:00
COMMANDS = " ${ BASHBOT_ETC :- . } /commands.sh "
2021-01-01 23:11:23 +00:00
if [ -r " ${ COMMANDS } " ] ; then
# shellcheck source=./commands.sh
source " ${ COMMANDS } " "source"
else
[ -z " ${ SOURCE } " ] && printf " ${ RED } Warning: ${ COMMANDS } does not exist or is not readable!. ${ NN } "
2019-05-10 09:33:41 +00:00
fi
2021-02-01 17:30:34 +00:00
# no debug checks on source
[ -z " ${ SOURCE } " ] && debug_checks "start" " $@ "
2019-05-10 09:33:41 +00:00
2020-06-10 06:20:48 +00:00
#####################
# BASHBOT INTERNAL functions
#
2020-06-12 19:18:32 +00:00
# do we have BSD sed
2021-01-01 23:11:23 +00:00
sed '1ia' </dev/null 2>/dev/null || printf " ${ ORANGE } Warning: You may run on a BSD style system without gnu utils ... ${ NN } "
2020-06-10 06:20:48 +00:00
#jsonDB is now mandatory
2021-01-01 23:32:32 +00:00
if ! _is_function jssh_newDB; then
printf " ${ RED } ERROR: Mandatory module jsonDB is missing or not readable! ${ NN } "
2021-02-01 08:37:22 +00:00
exit_source 6
2021-01-01 23:32:32 +00:00
fi
2020-06-12 19:18:32 +00:00
2019-05-19 08:52:44 +00:00
# $1 postfix, e.g. chatid
# $2 prefix, back- or startbot-
procname( ) {
2019-05-20 08:50:51 +00:00
printf '%s\n' " $2 ${ ME } _ $1 "
2019-05-19 08:52:44 +00:00
}
2020-09-26 17:52:06 +00:00
# $1 string to search for programme incl. parameters
# returns a list of PIDs of all current bot processes matching $1
2019-05-19 08:52:44 +00:00
proclist( ) {
# shellcheck disable=SC2009
2019-05-21 08:53:52 +00:00
ps -fu " ${ UID } " | grep -F " $1 " | grep -v ' grep' | grep -F " ${ ME } " | sed 's/\s\+/\t/g' | cut -f 2
2019-05-19 08:52:44 +00:00
}
2020-09-26 17:52:06 +00:00
# $1 string to search for programme to kill
2019-05-21 08:53:52 +00:00
killallproc( ) {
local procid; procid = " $( proclist " $1 " ) "
2020-05-14 18:33:30 +00:00
if [ -n " ${ procid } " ] ; then
2019-05-21 08:53:52 +00:00
# shellcheck disable=SC2046
kill $( proclist " $1 " )
sleep 1
procid = " $( proclist " $1 " ) "
# shellcheck disable=SC2046
2020-05-14 18:33:30 +00:00
[ -n " ${ procid } " ] && kill $( proclist -9 " $1 " )
2019-05-21 08:53:52 +00:00
fi
2021-01-05 15:17:34 +00:00
debug_checks "end killallproc" " $1 "
2019-05-21 08:53:52 +00:00
}
2021-02-06 10:04:39 +00:00
# URL path for file id, $1 file_id
# use download_file "path" to download file
2019-05-14 13:25:15 +00:00
get_file( ) {
2020-05-14 18:33:30 +00:00
[ -z " $1 " ] && return
2021-01-14 18:49:01 +00:00
sendJson "" '"file_id": "' " $1 " '"' " ${ URL } /getFile "
2021-02-06 10:04:39 +00:00
printf "%s\n" " ${ UPD [ "result,file_path" ] } "
2019-05-14 13:25:15 +00:00
}
2021-02-06 10:04:39 +00:00
# download file to DATADIR
# $1 URL path, $2 proposed filename (may modified/ignored)
# outputs final filename
# keep old function name for backward compatibility
alias download = "download_file"
download_file( ) {
local url = " $1 " file = " ${ 2 :- $1 } "
# old mode if full URL is given
if [ [ " ${ 1 } " = ~ ^https*:// ] ] ; then
2021-02-06 14:30:31 +00:00
# random filename if not given for http
if [ -z " $2 " ] ; then
: " $( mktemp -u -p . "XXXXXXXXXX" 2>/dev/null) "
file = " download- ${ _ #./ } "
fi
2021-02-06 10:04:39 +00:00
else
# prefix https://api.telegram...
2021-02-06 15:05:18 +00:00
url = " ${ FILEURL } / ${ url } "
2021-02-06 10:04:39 +00:00
fi
2021-02-06 14:30:31 +00:00
# filename: replace "/" with "-", use mktemp if exist
file = " ${ DATADIR :- . } / ${ file // \/ /- } "
[ -f " ${ file } " ] && file = " $( mktemp -p " ${ DATADIR :- . } " " XXXXX- ${ file ##*/ } " ) "
getJson " ${ url } " >" ${ file } " || return
2021-02-06 16:38:37 +00:00
# output absolute file path
printf "%s\n" " $( cd " ${ file %/* } " >/dev/null 2>& 1 && pwd ) / ${ file ##*/ } "
2021-02-06 10:04:39 +00:00
}
2021-02-08 16:55:43 +00:00
# notify mycommands about errors while sending
# $1 calling function $2 error $3 chat $4 user $5 error message $6 ... remaining args to calling function
# calls function based on error: bashbotError{function} basbotError{error}
# if no specific function exist try to call bashbotProcessError
processError( ) {
2021-02-08 17:41:54 +00:00
local func = " $1 " err = " $2 "
2021-02-08 18:56:01 +00:00
[ [ " ${ err } " != "4" * ] ] && return 1
2021-02-08 16:55:43 +00:00
# check for bashbotError${func} provided in mycommands
# shellcheck disable=SC2082
2021-02-08 17:41:54 +00:00
if _is_function " bashbotError_ ${ func } " ; then
" bashbotError_ ${ func } " " $@ "
2021-02-08 16:55:43 +00:00
# check for bashbotError${err} provided in mycommands
2021-02-08 17:41:54 +00:00
elif _is_function " bashbotError_ ${ err } " ; then
" bashbotError_ ${ err } " " $@ "
2021-02-08 16:55:43 +00:00
# noting found, try bashbotProcessError
else
2021-02-08 17:41:54 +00:00
_exec_if_function bashbotProcessError " $@ "
2021-02-08 16:55:43 +00:00
fi
}
2019-05-14 13:25:15 +00:00
2020-11-28 16:36:34 +00:00
# iconv used to filter out broken utf characters, if not installed fake it
if ! _exists iconv; then
2020-12-15 19:53:42 +00:00
log_update "Warning: iconv not installed, pls imstall iconv!"
2020-11-28 16:36:34 +00:00
function iconv( ) { cat; }
fi
2021-01-04 22:08:09 +00:00
TIMEOUT = " ${ BASHBOT_TIMEOUT :- 20 } "
[ [ " ${ TIMEOUT } " = ~ ^[ ${ o9o9o9 } ] +$ ] ] || TIMEOUT = "20"
2019-05-23 17:40:15 +00:00
2020-12-15 09:40:20 +00:00
# usage: sendJson "chat" "JSON" "URL"
sendJson( ) {
2020-12-15 09:07:43 +00:00
local json chat = ""
2021-01-05 15:17:34 +00:00
if [ -n " $1 " ] ; then
chat = '"chat_id":' " $1 " ','
2021-01-20 18:50:19 +00:00
[ [ " $1 " = = *[ !${ o9o9o9 } -] * ] ] && chat = '"chat_id":"' " $1 " ' NAN",' # chat id not a number!
2020-12-15 09:12:18 +00:00
fi
2020-12-18 13:23:20 +00:00
# compose final json
2020-12-15 09:07:43 +00:00
json = '{' " ${ chat } $( iconv -f utf-8 -t utf-8 -c <<< " $2 " ) " '}'
if [ -n " ${ BASHBOTDEBUG } " ] ; then
2021-02-21 22:34:34 +00:00
log_update " sendJson ( ${ DETECTED_CURL } ) CHAT= ${ chat #* : } JSON= $( cleanEscape " ${ json : 0 : 100 } " ) URL= ${ 3 ##*/ } "
log_message " DEBUG sendJson ==========\n $( " ${ JSONSHFILE } " -b -n <<< " $( cleanEscape " ${ json } " ) " 2>& 1) "
2020-12-15 09:07:43 +00:00
fi
2020-12-18 13:47:53 +00:00
# chat id not a number
if [ [ " ${ chat } " = = *"NAN\"," ] ] ; then
2020-12-18 13:23:20 +00:00
sendJsonResult " $( printf '["ok"]\tfalse\n["error_code"]\t400\n["description"]\t"Bad Request: chat id not a number"\n' ) " \
"sendJson (NAN)" " $@ "
2020-12-18 13:47:53 +00:00
return
2020-12-18 13:23:20 +00:00
fi
2020-12-15 09:40:20 +00:00
# OK here we go ...
# route to curl/wget specific function
2021-01-05 15:17:34 +00:00
res = " $( sendJson_do " ${ json } " " $3 " ) "
2020-12-15 09:40:20 +00:00
# check telegram response
2020-12-18 13:23:20 +00:00
sendJsonResult " ${ res } " " sendJson ( ${ DETECTED_CURL } ) " " $@ "
2020-06-26 07:07:00 +00:00
[ -n " ${ BASHBOT_EVENT_SEND [*] } " ] && event_send "send" " ${ @ } " &
2020-12-15 09:40:20 +00:00
}
2021-02-10 20:11:15 +00:00
UPLOADDIR = " ${ BASHBOT_UPLOAD :- ${ DATADIR } /upload } "
# $1 chat $2 file, $3 calling function
# return final file name or empty string on error
checkUploadFile( ) {
local err file = " $2 "
2021-02-17 09:20:45 +00:00
[ [ " ${ file } " = = *'..' * || " ${ file } " = = '.' * ] ] && err = 1 # no directory traversal
if [ [ " ${ file } " = = '/' * ] ] ; then
2021-02-10 20:11:15 +00:00
[ [ ! " ${ file } " = ~ ${ FILE_REGEX } ] ] && err = 2 # absolute must match REGEX
else
file = " ${ UPLOADDIR :- NOUPLOADDIR } / ${ file } " # others must be in UPLOADDIR
fi
[ ! -r " ${ file } " ] && err = 3 # and file must exits of course
# file path error, generate error response
if [ -n " ${ err } " ] ; then
BOTSENT = ( ) ; BOTSENT[ OK] = "false"
case " ${ err } " in
1) BOTSENT[ ERROR] = " Path to file $2 contains to much '../' or starts with '.' " ; ;
2) BOTSENT[ ERROR] = " Path to file $2 does not match regex: ${ FILE_REGEX } " ; ;
3) if [ [ " $2 " = = "/" * ] ] ; then
BOTSENT[ ERROR] = " File not found: $2 "
else
BOTSENT[ ERROR] = " File not found: ${ UPLOADDIR } / $2 "
fi ; ;
esac
[ -n " ${ BASHBOTDEBUG } " ] && log_debug " $3 : CHAT= $1 FILE= $2 MSG= ${ BOTSENT [DESCRIPTION] } "
return 1
fi
2021-02-17 09:20:45 +00:00
printf "%s\n" " ${ file } "
2021-02-10 20:11:15 +00:00
}
2021-01-29 19:48:31 +00:00
2020-12-15 09:40:20 +00:00
#
# curl / wget specific functions
#
if detect_curl ; then
# here we have curl ----
[ -z " ${ BASHBOT_CURL } " ] && BASHBOT_CURL = "curl"
2021-01-14 16:47:06 +00:00
# $1 URL, $2 hack: log getJson if not ""
2020-12-15 09:40:20 +00:00
getJson( ) {
# shellcheck disable=SC2086
2021-09-20 18:32:51 +00:00
" ${ BASHBOT_CURL } " -sL -k ${ BASHBOT_CURL_ARGS } -m " ${ TIMEOUT } " -K - <<< " url= $1 "
2020-12-15 09:40:20 +00:00
}
# curl variant for sendJson
# usage: "JSON" "URL"
sendJson_do( ) {
# shellcheck disable=SC2086
" ${ BASHBOT_CURL } " -s -k ${ BASHBOT_CURL_ARGS } -m " ${ TIMEOUT } " \
2021-09-20 18:32:51 +00:00
-d " $1 " -X POST -K - <<< " url= $2 " -H "Content-Type: application/json" | " ${ JSONSHFILE } " -b -n 2>/dev/null
2019-05-14 13:25:15 +00:00
}
2020-09-26 17:52:06 +00:00
#$1 Chat, $2 what, $3 file, $4 URL, $5 caption
2019-05-20 15:26:21 +00:00
sendUpload( ) {
[ " $# " -lt 4 ] && return
2020-05-14 18:33:30 +00:00
if [ -n " $5 " ] ; then
2021-01-05 21:01:32 +00:00
[ -n " ${ BASHBOTDEBUG } " ] && \
log_update " sendUpload CHAT= $1 WHAT= $2 FILE= $3 CAPT= $5 "
# shellcheck disable=SC2086
2021-09-20 18:32:51 +00:00
res = " $( " ${ BASHBOT_CURL } " -s -k ${ BASHBOT_CURL_ARGS } -K - <<< " url= $4 " -F " chat_id= $1 " \
2020-10-16 08:53:08 +00:00
-F " $2 =@ $3 ; ${ 3 ##*/ } " -F " caption= $5 " | " ${ JSONSHFILE } " -b -n 2>/dev/null ) "
2019-05-20 15:26:21 +00:00
else
2021-01-05 21:01:32 +00:00
# shellcheck disable=SC2086
2021-09-20 18:32:51 +00:00
res = " $( " ${ BASHBOT_CURL } " -s -k ${ BASHBOT_CURL_ARGS } -K - <<< " url= $4 " -F " chat_id= $1 " \
2020-10-16 08:53:08 +00:00
-F " $2 =@ $3 ; ${ 3 ##*/ } " | " ${ JSONSHFILE } " -b -n 2>/dev/null ) "
2019-05-20 15:26:21 +00:00
fi
2020-06-08 10:58:36 +00:00
sendJsonResult " ${ res } " "sendUpload (curl)" " $@ "
2020-06-26 07:07:00 +00:00
[ -n " ${ BASHBOT_EVENT_SEND [*] } " ] && event_send "upload" " $@ " &
2019-05-20 15:26:21 +00:00
}
2019-05-14 13:25:15 +00:00
else
2020-11-29 16:20:57 +00:00
# NO curl, try wget
if _exists wget; then
getJson( ) {
2019-05-23 17:40:15 +00:00
# shellcheck disable=SC2086
2021-09-20 18:32:51 +00:00
wget --no-check-certificate -t 2 -T " ${ TIMEOUT } " ${ BASHBOT_WGET_ARGS } -qO - -i <<< " $1 "
2020-11-29 16:20:57 +00:00
}
2020-12-15 09:40:20 +00:00
# curl variant for sendJson
# usage: "JSON" "URL"
sendJson_do( ) {
2019-05-23 17:40:15 +00:00
# shellcheck disable=SC2086
2021-01-05 15:17:34 +00:00
wget --no-check-certificate -t 2 -T " ${ TIMEOUT } " ${ BASHBOT_WGET_ARGS } -qO - --post-data= " $1 " \
2021-09-20 18:32:51 +00:00
--header= 'Content-Type:application/json' -i <<< " $2 " | " ${ JSONSHFILE } " -b -n 2>/dev/null
2020-11-29 16:20:57 +00:00
}
sendUpload( ) {
2020-06-27 08:43:08 +00:00
log_error "Sorry, wget does not support file upload"
2019-05-20 15:26:21 +00:00
BOTSENT[ OK] = "false"
2020-06-26 07:07:00 +00:00
[ -n " ${ BASHBOT_EVENT_SEND [*] } " ] && event_send "upload" " $@ " &
2020-11-29 16:20:57 +00:00
}
else
# ups, no curl AND no wget
if [ -n " ${ BASHBOT_WGET } " ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ RED } Error: You set BASHBOT_WGET but no wget found! ${ NN } "
2020-11-29 16:20:57 +00:00
else
2020-12-26 20:18:18 +00:00
printf " ${ RED } Error: curl and wget not found, install curl! ${ NN } "
2020-11-29 16:20:57 +00:00
fi
2021-02-01 08:37:22 +00:00
exit_source 8
2020-11-29 16:20:57 +00:00
fi
2019-05-14 13:25:15 +00:00
fi
2020-06-08 19:47:36 +00:00
# retry sendJson
2020-06-09 06:47:13 +00:00
# $1 function $2 sleep $3 ... $n arguments
2020-06-08 19:47:36 +00:00
sendJsonRetry( ) {
2021-01-05 15:17:34 +00:00
local retry = " $1 " ; shift
[ [ " $1 " = ~ ^\ *[ ${ o9o9o9 } .] +\ *$ ] ] && sleep " $1 " ; shift
2021-01-06 14:33:07 +00:00
printf "%(%c)T: RETRY %s %s %s\n" -1 " ${ retry } " " $1 " " ${ 2 : 0 : 60 } "
2020-06-08 19:47:36 +00:00
case " ${ retry } " in
'sendJson' *)
sendJson " $@ "
; ;
'sendUpload' *)
sendUpload " $@ "
; ;
2020-06-26 07:11:10 +00:00
'send_album' *)
send_album " $@ "
; ;
2020-06-08 19:47:36 +00:00
*)
2020-06-27 08:43:08 +00:00
log_error " Error: unknown function ${ retry } , cannot retry "
2020-06-10 13:56:34 +00:00
return
2020-06-08 19:47:36 +00:00
; ;
esac
2021-01-05 15:17:34 +00:00
[ " ${ BOTSENT [OK] } " = "true" ] && log_error " Retry OK: ${ retry } $1 ${ 2 : 0 : 60 } "
2020-06-10 13:56:34 +00:00
} >>" ${ ERRORLOG } "
2020-06-08 19:47:36 +00:00
# process sendJson result
2020-06-09 06:47:13 +00:00
# stdout is written to ERROR.log
# $1 result $2 function $3 .. $n original arguments, $3 is Chat_id
2020-06-08 10:58:36 +00:00
sendJsonResult( ) {
2020-06-20 18:12:36 +00:00
local offset = 0
2020-06-08 18:51:15 +00:00
BOTSENT = ( )
2021-01-15 18:26:04 +00:00
Json2Array 'UPD' <<< " $1 "
2020-12-15 19:53:42 +00:00
[ -n " ${ BASHBOTDEBUG } " ] && log_message " New Result ==========\n $1 "
2021-01-15 18:26:04 +00:00
BOTSENT[ OK] = " ${ UPD [ "ok" ] } "
2020-06-08 10:58:36 +00:00
if [ " ${ BOTSENT [OK] } " = "true" ] ; then
2021-01-19 16:47:15 +00:00
BOTSENT[ ID] = " ${ UPD [ "result,message_id" ] } "
BOTSENT[ CHAT] = " ${ UPD [ "result,chat,id" ] } "
2021-01-15 18:26:04 +00:00
[ -n " ${ UPD [ "result" ] } " ] && BOTSENT[ RESULT] = " ${ UPD [ "result" ] } "
2020-06-08 10:58:36 +00:00
return
2020-06-23 14:35:50 +00:00
# hot path everything OK!
2020-06-08 10:58:36 +00:00
else
2020-06-08 18:51:15 +00:00
# oops something went wrong!
2021-01-06 14:33:07 +00:00
if [ -n " $1 " ] ; then
2021-01-15 18:26:04 +00:00
BOTSENT[ ERROR] = " ${ UPD [ "error_code" ] } "
BOTSENT[ DESCRIPTION] = " ${ UPD [ "description" ] } "
2021-01-19 16:47:15 +00:00
[ -n " ${ UPD [ "parameters,retry_after" ] } " ] && BOTSENT[ RETRY] = " ${ UPD [ "parameters,retry_after" ] } "
2020-06-08 10:58:36 +00:00
else
2020-09-26 17:52:06 +00:00
BOTSENT[ OK] = "false"
BOTSENT[ ERROR] = "999"
BOTSENT[ DESCRIPTION] = "Send to telegram not possible, timeout/broken/no connection"
2020-06-08 10:58:36 +00:00
fi
2020-06-08 18:51:15 +00:00
# log error
2020-06-20 18:12:36 +00:00
[ [ " ${ BOTSENT [ERROR] } " = "400" && " ${ BOTSENT [DESCRIPTION] } " = = *"starting at byte offset" * ] ] && \
offset = " ${ BOTSENT [DESCRIPTION]%* } "
2021-01-06 14:33:07 +00:00
printf "%(%c)T: RESULT=%s FUNC=%s CHAT[ID]=%s ERROR=%s DESC=%s ACTION=%s\n" -1\
2021-01-05 15:17:34 +00:00
" ${ BOTSENT [OK] } " " $2 " " $3 " " ${ BOTSENT [ERROR] } " " ${ BOTSENT [DESCRIPTION] } " " ${ 4 : ${ offset } : 100 } "
2020-06-09 10:27:28 +00:00
# warm path, do not retry on error, also if we use wegt
2020-06-11 06:33:59 +00:00
[ -n " ${ BASHBOT_RETRY } ${ BASHBOT_WGET } " ] && return
2020-06-08 19:47:36 +00:00
2020-06-09 06:47:13 +00:00
# OK, we can retry sendJson, let's see what's failed
2020-09-26 17:52:06 +00:00
# throttled, telegram say we send too many messages
2020-06-08 18:51:15 +00:00
if [ -n " ${ BOTSENT [RETRY] } " ] ; then
2020-09-26 17:52:06 +00:00
BASHBOT_RETRY = " $(( + + BOTSENT[ RETRY] )) "
2021-01-05 15:17:34 +00:00
printf "Retry %s in %s seconds ...\n" " $2 " " ${ BASHBOT_RETRY } "
sendJsonRetry " $2 " " ${ BASHBOT_RETRY } " " ${ @ : 3 } "
2020-09-26 17:52:06 +00:00
unset BASHBOT_RETRY
return
2020-06-08 18:51:15 +00:00
fi
2020-06-08 19:47:36 +00:00
# timeout, failed connection or blocked
2020-06-08 18:51:15 +00:00
if [ " ${ BOTSENT [ERROR] } " = = "999" ] ; then
# check if default curl and args are OK
2021-03-24 10:06:30 +00:00
if ! curl -sL -k -m 2 " ${ URL } " >/dev/null 2>& 1 ; then
printf "%(%c)T: BASHBOT IP Address seems blocked!\n" -1
# user provided function to recover or notify block
if _exec_if_function bashbotBlockRecover; then
BASHBOT_RETRY = "2"
printf "bashbotBlockRecover returned true, retry %s ...\n" " $2 "
2021-01-05 15:17:34 +00:00
sendJsonRetry " $2 " " ${ BASHBOT_RETRY } " " ${ @ : 3 } "
2020-09-26 17:52:06 +00:00
unset BASHBOT_RETRY
fi
2021-03-24 10:06:30 +00:00
# seems not blocked, try if blockrecover and default curl args working
elif [ -n " ${ BASHBOT_CURL_ARGS } " ] || [ " ${ BASHBOT_CURL } " != "curl" ] ; then
printf "Problem with \"%s %s\"? retry %s with default config ...\n" \
" ${ BASHBOT_CURL } " " ${ BASHBOT_CURL_ARGS } " " $2 "
BASHBOT_RETRY = "2" ; BASHBOT_CURL = "curl" ; BASHBOT_CURL_ARGS = ""
_exec_if_function bashbotBlockRecover
sendJsonRetry " $2 " " ${ BASHBOT_RETRY } " " ${ @ : 3 } "
unset BASHBOT_RETRY
fi
[ -n " ${ BOTSENT [ERROR] } " ] && processError " $3 " " ${ BOTSENT [ERROR] } " " $4 " "" " ${ BOTSENT [DESCRIPTION] } " " $5 " " $6 "
2020-06-08 18:51:15 +00:00
fi
2020-06-08 10:58:36 +00:00
fi
2020-06-09 06:47:13 +00:00
} >>" ${ ERRORLOG } "
2020-06-08 10:58:36 +00:00
2019-05-09 14:48:38 +00:00
# convert common telegram entities to JSON
# title caption description markup inlinekeyboard
title2Json( ) {
local title caption desc markup keyboard
2020-05-14 18:33:30 +00:00
[ -n " $1 " ] && title = ',"title":"' $( JsonEscape " $1 " ) '"'
[ -n " $2 " ] && caption = ',"caption":"' $( JsonEscape " $2 " ) '"'
[ -n " $3 " ] && desc = ',"description":"' $( JsonEscape " $3 " ) '"'
2020-06-15 12:22:28 +00:00
[ -n " $4 " ] && markup = ',"parse_mode":"' " $4 " '"'
2020-05-14 18:33:30 +00:00
[ -n " $5 " ] && keyboard = ',"reply_markup":"' $( JsonEscape " $5 " ) '"'
2020-06-09 07:15:36 +00:00
printf '%s\n' " ${ title } ${ caption } ${ desc } ${ markup } ${ keyboard } "
2019-05-09 14:48:38 +00:00
}
2020-12-03 19:43:20 +00:00
# get bot name and id from telegram
2019-05-14 13:25:15 +00:00
getBotName( ) {
2020-12-03 13:07:39 +00:00
declare -A BOTARRAY
2021-01-04 22:08:09 +00:00
Json2Array 'BOTARRAY' <<< " $( getJson " ${ ME_URL } " | " ${ JSONSHFILE } " -b -n 2>/dev/null) "
2020-12-03 19:43:20 +00:00
[ -z " ${ BOTARRAY [ "result" , "username" ] } " ] && return 1
2020-12-03 13:07:39 +00:00
# save botname and id
setConfigKey "botname" " ${ BOTARRAY [ "result" , "username" ] } "
setConfigKey "botid" " ${ BOTARRAY [ "result" , "id" ] } "
2020-12-26 20:18:18 +00:00
printf " ${ BOTARRAY [ "result" , "username" ] } \n "
2016-01-06 16:11:56 +00:00
}
2020-06-23 14:35:50 +00:00
# pure bash implementation, done by KayM (@gnadelwartz)
2019-05-14 15:56:23 +00:00
# see https://stackoverflow.com/a/55666449/9381171
JsonDecode( ) {
2021-01-15 12:09:32 +00:00
local remain U out = " $1 "
2019-05-14 15:56:23 +00:00
local regexp = '(.*)\\u[dD]([0-9a-fA-F]{3})\\u[dD]([0-9a-fA-F]{3})(.*)'
2021-01-04 22:08:09 +00:00
while [ [ " ${ out } " = ~ ${ regexp } ] ] ; do
2020-09-26 17:52:06 +00:00
U = $(( ( ( 0 xd${ BASH_REMATCH [2] } & 0 x3ff) <<10 ) | ( 0xd${BASH_REMATCH[3]} & 0x3ff ) + 0x10 000 ))
remain = " $( printf '\\U%8.8x' " ${ U } " ) ${ BASH_REMATCH [4] } ${ remain } "
out = " ${ BASH_REMATCH [1] } "
done
2021-01-15 12:09:32 +00:00
printf "%b\n" " ${ out } ${ remain } "
2019-05-14 15:56:23 +00:00
}
2019-05-14 13:25:15 +00:00
2019-06-04 16:04:52 +00:00
EVENT_SEND = "0"
2021-01-29 19:48:31 +00:00
declare -Ax BASHBOT_EVENT_SEND
2019-06-04 16:04:52 +00:00
event_send( ) {
# max recursion level 5 to avoid fork bombs
2021-01-04 22:08:09 +00:00
( ( EVENT_SEND++ ) ) ; [ " ${ EVENT_SEND } " -gt "5" ] && return
2019-06-04 16:04:52 +00:00
# shellcheck disable=SC2153
for key in " ${ !BASHBOT_EVENT_SEND[@] } "
do
_exec_if_function " ${ BASHBOT_EVENT_SEND [ ${ key } ] } " " $@ "
done
}
2021-03-04 12:58:04 +00:00
# cleanup activities on startup, called from startbot and resume background jobs
# $1 action, timestamp for action is saved in config
bot_cleanup( ) {
# cleanup countfile on startup
jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" " ${ COUNTFILE } "
[ -f " ${ COUNTFILE } .jssh.flock " ] && rm -f " ${ COUNTFILE } .jssh.flock "
# store action time and cleanup botconfig on startup
[ -n " $1 " ] && jssh_updateKeyDB " $1 " " $( _date) " " ${ BOTCONFIG } "
[ -f " ${ BOTCONFIG } .jssh.flock " ] && rm -f " ${ BOTCONFIG } .jssh.flock "
}
2019-04-22 19:05:52 +00:00
2021-01-27 15:19:43 +00:00
# fallback version, full version is in bin/bashbot_init.in.sh
2019-04-22 19:05:52 +00:00
# initialize bot environment, user and permissions
bot_init( ) {
2021-02-01 18:37:24 +00:00
if [ -n " ${ BASHBOT_HOME } " ] && ! cd " ${ BASHBOT_HOME } " ; then
printf "Can't change to BASHBOT_HOME"
exit 1
fi
2021-02-01 11:58:57 +00:00
# initialize addons
2021-01-27 15:29:58 +00:00
printf "Initialize addons ...\n"
2019-12-07 12:25:50 +00:00
for addons in " ${ ADDONDIR :- . } " /*.sh ; do
2019-05-25 17:31:20 +00:00
# shellcheck source=./modules/aliases.sh
2021-01-27 15:19:43 +00:00
[ -r " ${ addons } " ] && source " ${ addons } " "init" " $1 "
2019-05-25 17:31:20 +00:00
done
2020-12-26 20:18:18 +00:00
printf "Done.\n"
2021-01-10 17:23:37 +00:00
# adjust permissions
2021-01-27 15:19:43 +00:00
printf "Adjusting files and permissions ...\n"
2021-01-10 17:23:37 +00:00
chmod 711 .
chmod -R o-w ./*
chmod -R u+w " ${ COUNTFILE } " * " ${ BLOCKEDFILE } " * " ${ DATADIR } " logs " ${ LOGDIR } / " *.log 2>/dev/null
chmod -R o-r,o-w " ${ COUNTFILE } " * " ${ BLOCKEDFILE } " * " ${ DATADIR } " " ${ BOTACL } " 2>/dev/null
# jsshDB must writeable by owner
find . -name '*.jssh*' -exec chmod u+w \{ \} +
printf "Done.\n"
2021-01-27 15:19:43 +00:00
_exec_if_function my_init
2019-04-22 19:05:52 +00:00
}
2019-05-26 15:03:58 +00:00
if ! _is_function send_message ; then
2020-12-26 20:18:18 +00:00
printf " ${ RED } ERROR: send_message is not available, did you deactivate ${ MODULEDIR } /sendMessage.sh? ${ NN } "
2021-02-01 08:37:22 +00:00
exit_source 1
2019-05-26 15:03:58 +00:00
fi
2020-12-15 15:36:27 +00:00
# check if JSON.awk exist and has x flag
JSONAWKFILE = " ${ JSONSHFILE %.sh } .awk "
if [ -x " ${ JSONAWKFILE } " ] && _exists awk ; then
JSONSHFILE = "JsonAwk" ; JsonAwk( ) { " ${ JSONAWKFILE } " -v "BRIEF=8" -v "STRICT=0" -; }
fi
2016-03-20 21:34:55 +00:00
# source the script with source as param to use functions in other scripts
2019-04-20 14:26:16 +00:00
# do not execute if read from other scripts
2021-03-03 12:27:07 +00:00
BOTADMIN = " $( getConfigKey "botadmin" ) "
2020-06-19 16:47:18 +00:00
if [ -z " ${ SOURCE } " ] ; then
2019-04-22 19:05:52 +00:00
##############
# internal options only for use from bashbot and developers
2020-11-29 14:34:00 +00:00
# shellcheck disable=SC2221,SC2222
2021-01-05 15:17:34 +00:00
case " $1 " in
2020-09-26 17:52:06 +00:00
# update botname when starting only
2020-06-22 12:10:36 +00:00
"botname" | "start" *)
2020-06-12 19:18:32 +00:00
ME = " $( getBotName) "
if [ -n " ${ ME } " ] ; then
2020-09-26 17:52:06 +00:00
# ok we have a connection and got botname, save it
2020-12-27 10:26:38 +00:00
[ -n " ${ INTERACTIVE } " ] && printf " ${ GREY } Bottoken is valid ... ${ NN } "
2020-06-19 10:49:18 +00:00
jssh_updateKeyDB "botname" " ${ ME } " " ${ BOTCONFIG } "
rm -f " ${ BOTCONFIG } .jssh.flock "
2020-06-12 19:18:32 +00:00
else
2020-12-26 20:18:18 +00:00
printf " ${ GREY } Info: Can't get Botname from Telegram, try cached one ... ${ NN } "
2020-06-12 19:18:32 +00:00
ME = " $( getConfigKey "botname" ) "
2021-01-04 22:08:09 +00:00
if [ -z " ${ ME } " ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ RED } ERROR: No cached botname, can't continue! ... ${ NN } "
2020-06-12 19:18:32 +00:00
exit 1
fi
fi
2020-12-27 10:26:38 +00:00
[ -n " ${ INTERACTIVE } " ] && printf "Bot Name: %s\n" " ${ ME } "
2020-06-12 19:18:32 +00:00
[ " $1 " = "botname" ] && exit
2020-06-12 21:41:47 +00:00
; ; &
2020-06-23 14:35:50 +00:00
# used to send output of background and interactive to chats
2021-01-20 18:50:19 +00:00
"outproc" ) # $2 chat_id $3 identifier of job, internal use only!
2020-12-26 20:18:18 +00:00
[ -z " $3 " ] && printf "No job identifier\n" && exit 3
[ -z " $2 " ] && printf "No chat to send to\n" && exit 3
2020-06-19 10:54:05 +00:00
ME = " $( getConfigKey "botname" ) "
2020-06-12 19:18:32 +00:00
# read until terminated
2019-05-19 15:31:55 +00:00
while read -r line ; do
2021-01-04 22:08:09 +00:00
[ -n " ${ line } " ] && send_message " $2 " " ${ line } "
2019-05-18 18:25:18 +00:00
done
2020-06-12 19:18:32 +00:00
# cleanup datadir, keep logfile if not empty
2019-05-28 18:44:40 +00:00
rm -f -r " ${ DATADIR :- . } / $3 "
[ -s " ${ DATADIR :- . } / $3 .log " ] || rm -f " ${ DATADIR :- . } / $3 .log "
2020-06-27 08:29:36 +00:00
debug_checks "end outproc" " $@ "
2019-04-22 19:05:52 +00:00
exit
; ;
2021-01-29 19:48:31 +00:00
# finally starts the read update loop, internal use only
2019-04-22 19:05:52 +00:00
"startbot" )
2021-03-03 17:37:57 +00:00
_exec_if_function start_bot " $2 " "polling mode"
2021-02-28 15:51:28 +00:00
_exec_if_function get_updates " $2 "
2020-06-27 08:29:36 +00:00
debug_checks "end startbot" " $@ "
2019-04-22 19:05:52 +00:00
exit
2016-04-16 18:50:05 +00:00
; ;
2020-06-12 19:18:32 +00:00
# run after every update to update files and adjust permissions
"init" )
2021-01-27 12:58:55 +00:00
# shellcheck source=./bin/bashbot._init.inc.sh"
[ -r " ${ BASHBOT_HOME :- . } /bin/bashbot_init.inc.sh " ] && source " ${ BASHBOT_HOME :- . } /bin/bashbot_init.inc.sh "
2019-05-25 17:31:20 +00:00
bot_init " $2 "
2020-06-27 08:29:36 +00:00
debug_checks "end init" " $@ "
2019-04-22 19:05:52 +00:00
exit
; ;
2020-12-27 10:18:42 +00:00
# stats deprecated
"stats" | "count" )
printf " ${ ORANGE } Stats is a separate command now, see bin/bashbot_stats.sh --help ${ NN } "
" ${ BASHBOT_HOME :- . } " /bin/bashbot_stats.sh --help
2019-04-04 13:25:27 +00:00
exit
2016-04-16 18:50:05 +00:00
; ;
2020-12-27 10:18:42 +00:00
# broadcast deprecated
2020-05-29 18:11:51 +00:00
'broadcast' )
2020-12-27 10:18:42 +00:00
printf " ${ ORANGE } Broadcast is a separate command now, see bin/send_broadcast.sh --help ${ NN } "
2020-12-17 07:58:32 +00:00
" ${ BASHBOT_HOME :- . } " /bin/send_broadcast.sh --help
2020-05-29 18:11:51 +00:00
exit
2016-04-16 18:50:05 +00:00
; ;
2020-09-26 17:52:06 +00:00
# does what it says
2019-05-19 13:03:44 +00:00
"status" )
2020-06-19 10:54:05 +00:00
ME = " $( getConfigKey "botname" ) "
SESSION = " ${ ME :- _bot } -startbot "
BOTPID = " $( proclist " ${ SESSION } " ) "
2020-05-14 18:33:30 +00:00
if [ -n " ${ BOTPID } " ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ GREEN } Bot is running with UID ${ RUNUSER } . ${ NN } "
2019-05-19 13:03:44 +00:00
exit
else
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } No Bot running with UID ${ RUNUSER } . ${ NN } "
2019-05-19 13:03:44 +00:00
exit 5
fi
2020-06-27 08:29:36 +00:00
debug_checks "end status" " $@ "
2019-05-19 13:03:44 +00:00
; ;
2020-09-26 17:52:06 +00:00
# start bot as background job and check if bot is running
2016-04-17 12:13:28 +00:00
"start" )
2021-01-29 19:55:54 +00:00
SESSION = " ${ ME :- _bot } -startbot "
BOTPID = " $( proclist " ${ SESSION } " ) "
2021-01-31 11:40:13 +00:00
if _is_function process_update; then
2021-01-29 19:48:31 +00:00
# shellcheck disable=SC2086
2021-02-01 08:02:22 +00:00
[ -n " ${ BOTPID } " ] && kill ${ BOTPID } && printf " ${ GREY } Stop already running bot ... ${ NN } "
2021-01-29 19:48:31 +00:00
nohup " ${ SCRIPT } " "startbot" " $2 " " ${ SESSION } " & >/dev/null &
printf "Session Name: %s\n" " ${ SESSION } "
sleep 1
else
printf " ${ ORANGE } Update processing disabled, bot can only send messages. ${ NN } "
2021-02-01 08:02:22 +00:00
[ -n " ${ BOTPID } " ] && printf " ${ ORANGE } Already running bot found ... ${ NN } "
2021-01-29 19:48:31 +00:00
fi
2020-05-14 18:33:30 +00:00
if [ -n " $( proclist " ${ SESSION } " ) " ] ; then
2020-12-26 20:18:18 +00:00
printf " ${ GREEN } Bot started successfully. ${ NN } "
2019-05-19 13:03:44 +00:00
else
2020-12-26 20:18:18 +00:00
printf " ${ RED } An error occurred while starting the bot. ${ NN } "
2019-05-19 13:03:44 +00:00
exit 5
fi
2020-06-27 08:29:36 +00:00
debug_checks "end start" " $@ "
2016-04-17 12:13:28 +00:00
; ;
2020-06-12 19:18:32 +00:00
# does what it says
"stop" )
2020-06-19 10:54:05 +00:00
ME = " $( getConfigKey "botname" ) "
SESSION = " ${ ME :- _bot } -startbot "
BOTPID = " $( proclist " ${ SESSION } " ) "
2020-05-14 18:33:30 +00:00
if [ -n " ${ BOTPID } " ] ; then
2019-05-19 13:03:44 +00:00
# shellcheck disable=SC2086
if kill ${ BOTPID } ; then
2020-06-15 18:07:43 +00:00
# inform botadmin about stop
2021-03-03 17:37:57 +00:00
send_normal_message " ${ BOTADMIN } " " Bot ${ ME } polling mode stopped ... " &
2020-12-26 20:18:18 +00:00
printf " ${ GREEN } OK. Bot stopped successfully. ${ NN } "
2019-05-19 13:03:44 +00:00
else
2020-12-26 20:18:18 +00:00
printf " ${ RED } An error occurred while stopping bot. ${ NN } "
2019-05-19 13:03:44 +00:00
exit 5
fi
2020-06-12 21:41:47 +00:00
else
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } No Bot running with UID ${ RUNUSER } . ${ NN } "
2019-05-19 13:03:44 +00:00
fi
2020-06-27 08:29:36 +00:00
debug_checks "end stop" " $@ "
2019-05-19 13:03:44 +00:00
exit
2019-03-25 10:15:07 +00:00
; ;
2020-06-23 14:35:50 +00:00
# suspend, resume or kill background jobs
2021-02-25 18:58:00 +00:00
"suspendb" *| "resumeb" *| 'restartb' *| "killb" *)
2020-12-26 20:18:18 +00:00
_is_function job_control || { printf " ${ RED } Module background is not available! ${ NN } " ; exit 3; }
2020-06-22 12:10:36 +00:00
ME = " $( getConfigKey "botname" ) "
2019-05-20 19:40:14 +00:00
job_control " $1 "
2020-06-27 08:29:36 +00:00
debug_checks " end background $1 " " $@ "
2019-03-18 21:19:44 +00:00
; ;
2016-04-19 09:49:35 +00:00
*)
2020-12-26 20:18:18 +00:00
printf " ${ RED } ${ REALME ##*/ } : unknown command ${ NN } "
2021-01-03 21:39:36 +00:00
printf " ${ ORANGE } Available commands: ${ GREY } ${ BOTCOMMANDS } ${ NN } " && exit
2019-05-23 17:40:15 +00:00
exit 4
2016-04-17 12:13:28 +00:00
; ;
2019-04-20 14:26:16 +00:00
esac
2016-06-05 10:41:49 +00:00
2019-04-20 14:26:16 +00:00
# warn if root
2019-05-19 13:03:44 +00:00
if [ [ " ${ UID } " -eq "0" ] ] ; then
2021-01-03 21:39:36 +00:00
printf " \n ${ ORANGE } WARNING: ${ SCRIPT } was started as ROOT (UID 0)! ${ NN } "
2020-12-26 20:18:18 +00:00
printf " ${ ORANGE } You are at HIGH RISK when running a Telegram BOT with root privileges! ${ NN } "
2019-04-20 14:26:16 +00:00
fi
fi # end source