diff --git a/README.html b/README.html index dc43b96..a51e2cb 100644 --- a/README.html +++ b/README.html @@ -223,8 +223,8 @@ Written by Drew (@topkecleon) and Kay M (@gnadelwartz).

Released to the public domain wherever applicable. Elsewhere, consider it released under the WTFPLv2.

Linted by #ShellCheck

Prerequisites

-

Uses JSON.sh and the magic of sed.

-

Bashbot is written in bash. It depends on commands typically available in a Linux/Unix Environment. For more concrete information on the common commands provided by recent versions of coreutils, busybox or toybox, see Developer Notes.

+

Uses JSON.sh/JSON.awk and the magic of sed.

+

Bashbot is written in bash. It depends on commands typically available in a Linux/Unix Environment. For more information on commands provided by recent versions of coreutils, busybox or toybox, see Developer Notes.

Note for MacOS and BSD Users: Bashbot will not run without installing additional software as it uses modern bash and (gnu) grep/sed features. See Install Bashbot.

Note for embedded systems: You need to install a "real" bash as the vanilla installation of busybox or toybox is not sufficient. See Install Bashbot.

Bashbot Documentation and Downloads are available on www.github.com.

@@ -284,7 +284,8 @@ Written by Drew (@topkecleon) and Kay M (@gnadelwartz).
  • Setup your environment
  • Bashbot test suite
  • -
  • Examples Directory
  • +
  • Examples Directory
  • +
  • Webhook Example
  • Your very first bashbot in a nutshell

    To install and run bashbot you need access to a Linux/Unix command line with bash, a Telegram client and a mobile phone with a Telegram account.

    @@ -340,7 +341,7 @@ It features background tasks and interactive chats, and can serve as an interfac

    Running a Telegram Bot means it is connected to the public and you never know what's send to your Bot.

    Bash scripts in general are not designed to be bulletproof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see Implications of wrong quoting.

    Whenever you are processing input from untrusted sources (messages, files, network) you must be as careful as possible (e.g. set IFS appropriately, disable globbing with set -f and quote everything). In addition remove unused scripts and examples from your Bot (e.g. everything in example/) and disable/remove all unused bot commands.

    -

    It's important to escape or remove $ in input from user, files or network (as bashbot does). One of the powerful features of Unix shells is variable and command substitution using ${} and$() can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped $ is included in untrusted input (e.g. $$ or $(rm -rf /*)).

    +

    It's important to escape or remove $ in input from user, files or network (as bashbot does). One of the powerful features of Unix shells is variable and command substitution using ${} and $() can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped $ is included in untrusted input (e.g. $$ or $(rm -rf /*)).

    A powerful tool to improve your scripts is shellcheck. You can use it online or install shellcheck locally. Shellcheck is used extensively in bashbot development to ensure a high code quality (e.g. it's not allowed to push changes without passing all shellcheck tests). In addition bashbot has a test suite to check if important functionality is working as expected.

    Use printf whenever possible

    If you're writing a script that accepts external input (e.g. from the user as arguments or the file system), you shouldn't use echo to display it. Use printf whenever possible.

    @@ -349,8 +350,9 @@ It features background tasks and interactive chats, and can serve as an interfac

    Never run your Bot as root, this is the most dangerous you can do! Usually the user 'nobody' has almost no rights on Linux/Unix systems. See Expert use on how to run your Bot as an other user.

    Secure your Bot installation

    Your Bot configuration must not be readable by other users. Everyone who can read your Bots token is able to act as your Bot and has access to all chats the Bot is in!

    -

    Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config inconfig.jssh must be protected against other users. No one except you should have write access to the Bot files. The Bot should be restricted to have write access tocount.jssh and data-bot-bash only, all other files must be write protected.

    -

    To set access rights for your bashbot installation to a reasonable default runsudo ./bashbot.sh init after every update or change to your installation directory.

    +

    Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config in config.jssh must be protected against other users. No one except you should have write access to the Bot files. The Bot should be restricted to have write access to count.jssh, data-bot-bash/ and logs/ only, all other files must be write protected.

    +

    To set access rights for your bashbot installation to a reasonable default run sudo ./bashbot.sh init after every update or change to your installation directory.

    +

    Note: Keep old log files in a safe place or even better delete them, they are GDPR relevant and may contain information you don't want to be public.

    FAQ

    Is this Bot insecure?

    Bashbot is not more (in)secure than a Bot written in another language. We have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks ...

    @@ -364,8 +366,8 @@ It features background tasks and interactive chats, and can serve as an interfac
  • no database, not event driven, not object oriented ...
  • Can I have the single bashbot.sh file back?

    -

    At the beginning bashbot was simply the filebashbot.sh that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.

    -

    Hey no problem, if you are finished with your cool bot, rundev/make-standalone.sh to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see Create a stripped down version of your Bot.

    +

    At the beginning bashbot was simply the file bashbot.sh that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.

    +

    Hey no problem, if you are finished with your cool bot, run dev/make-standalone.sh to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see Create a stripped down version of your Bot.

    Can I send messages from CLI and scripts?

    Of course you can send messages from command line and scripts! Simply install bashbot as described here, send the message '/start' to set yourself as botadmin and then stop the bot with ./bashbot.sh stop.

    Bashbot provides some ready to use scripts for sending messages from command line in bin/ dir, e.g. send_message.sh.

    @@ -390,6 +392,6 @@ It features background tasks and interactive chats, and can serve as an interfac

    @Gnadelwartz

    That's it all guys!

    If you feel that there's something missing or if you found a bug, feel free to submit a pull request!

    -

    $$VERSION$$ v1.30-0-g3266427

    +

    $$VERSION$$ v1.40-dev-34-g1440d56

    diff --git a/README.md b/README.md index 87c5d05..f5e0789 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ Elsewhere, consider it released under the [WTFPLv2](http://www.wtfpl.net/txt/cop Linted by [#ShellCheck](https://github.com/koalaman/shellcheck) ## Prerequisites -Uses [JSON.sh](http://github.com/dominictarr/JSON.sh) and the magic of sed. +Uses [JSON.sh](http://github.com/dominictarr/JSON.sh)/[JSON.awk](https://github.com/step-/JSON.awk) and the magic of sed. Bashbot is written in bash. It depends on commands typically available in a Linux/Unix Environment. -For more concrete information on the common commands provided by recent versions of [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or [toybox](https://landley.net/toybox/help.html), see [Developer Notes](doc/7_develop.md#common-commands). +For more information on commands provided by recent versions of [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or [toybox](https://landley.net/toybox/help.html), see [Developer Notes](doc/7_develop.md#common-commands). **Note for MacOS and BSD Users:** Bashbot will not run without installing additional software as it uses modern bash and (gnu) grep/sed features. See [Install Bashbot](doc/0_install.md). @@ -63,7 +63,8 @@ Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Do * Modules, addons, events * Setup your environment * Bashbot test suite -* [Examples Directory](examples/README.md) +* [Examples Directory](examples) +* [Webhook Example](examples/webhook) ### Your very first bashbot in a nutshell @@ -146,7 +147,7 @@ Whenever you are processing input from untrusted sources (messages, files, netwo from your Bot (e.g. everything in `example/`) and disable/remove all unused bot commands. It's important to escape or remove `$` in input from user, files or network (_as bashbot does_). -One of the powerful features of Unix shells is variable and command substitution using `${}` and`$()` can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped `$` is included in untrusted input (e.g. `$$` or `$(rm -rf /*)`). +One of the powerful features of Unix shells is variable and command substitution using `${}` and `$()` can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped `$` is included in untrusted input (e.g. `$$` or `$(rm -rf /*)`). A powerful tool to improve your scripts is `shellcheck`. You can [use it online](https://www.shellcheck.net/) or [install shellcheck locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used extensively in bashbot development @@ -168,9 +169,11 @@ For the same reason every file your Bot can read is in danger of being disclosed ### Secure your Bot installation **Your Bot configuration must not be readable by other users.** Everyone who can read your Bots token is able to act as your Bot and has access to all chats the Bot is in! -Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config in`config.jssh` must be protected against other users. No one except you should have write access to the Bot files. The Bot should be restricted to have write access to`count.jssh` and `data-bot-bash` only, all other files must be write protected. +Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config in `config.jssh` must be protected against other users. No one except you should have write access to the Bot files. The Bot should be restricted to have write access to `count.jssh`, `data-bot-bash/` and `logs/` only, all other files must be write protected. -To set access rights for your bashbot installation to a reasonable default run`sudo ./bashbot.sh init` after every update or change to your installation directory. +To set access rights for your bashbot installation to a reasonable default run `sudo ./bashbot.sh init` after every update or change to your installation directory. + +*Note*: Keep old log files in a safe place or even better delete them, they are GDPR relevant and [may contain information](https://github.com/topkecleon/telegram-bot-bash/issues/174) you don't want to be public. ## FAQ @@ -188,9 +191,9 @@ Well, that's a damn good question... maybe because I'm a Unix admin from the sto - no database, not event driven, not object oriented ... ### Can I have the single bashbot.sh file back? -At the beginning bashbot was simply the file`bashbot.sh` that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more. +At the beginning bashbot was simply the file `bashbot.sh` that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more. -Hey no problem, if you are finished with your cool bot, run`dev/make-standalone.sh` to create a stripped down version of your bot containing only +Hey no problem, if you are finished with your cool bot, run `dev/make-standalone.sh` to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see [Create a stripped down version of your Bot](doc/7_develop.md). ### Can I send messages from CLI and scripts? @@ -238,4 +241,4 @@ See `mycommnds.sh.dist` for an example. If you feel that there's something missing or if you found a bug, feel free to submit a pull request! -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/README.txt b/README.txt index c558942..9d6fbcb 100644 --- a/README.txt +++ b/README.txt @@ -39,18 +39,21 @@ Linted by #ShellCheck Prerequisites -Uses JSON.sh [http://github.com/dominictarr/JSON.sh] and the magic of sed. +Uses JSON.sh [http://github.com/dominictarr/JSON.sh]/JSON.awk [https://github.com/step-/ +JSON.awk] and the magic of sed. Bashbot is written in bash. It depends on commands typically available in a Linux/Unix -Environment. For more concrete information on the common commands provided by recent -versions of coreutils [https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands], -busybox or toybox, see Developer_Notes. +Environment. For more information on commands provided by recent versions of coreutils +[https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands], busybox [https:// +en.wikipedia.org/wiki/BusyBox#Commands] or toybox [https://landley.net/toybox/help.html], +see Developer Notes [doc/7_develop.md#common-commands]. Note for MacOS and BSD Users: Bashbot will not run without installing additional software as it uses modern bash and (gnu) grep/sed features. See Install Bashbot [doc/ 0_install.md]. Note for embedded systems: You need to install a "real" bash as the vanilla installation of busybox or toybox is not sufficient. See Install Bashbot [doc/0_install.md]. -Bashbot Documentation [https://github.com/topkecleon/telegram-bot-bash] and Downloads are -available on www.github.com. +Bashbot Documentation [https://github.com/topkecleon/telegram-bot-bash] and Downloads +[https://github.com/topkecleon/telegram-bot-bash/releases] are available on www.github.com +[https://www.github.com]. Documentation @@ -109,13 +112,15 @@ Documentation o Setup your environment o Bashbot test suite -* Examples Directory [examples/README.md] +* Examples Directory [examples] +* Webhook Example [examples/webhook] Your very first bashbot in a nutshell To install and run bashbot you need access to a Linux/Unix command line with bash, a -Telegram client [https://telegram.org] and a mobile phone with_a_Telegram_account. +Telegram client [https://telegram.org] and a mobile phone with a Telegram account [https:/ +/telegramguide.com/create-a-telegram-account/]. First you need to create a new Telegram Bot token [doc/1_firstbot.md] for your bot and write it down. Now open a Linux/Unix terminal with bash, create a new directory, change to it and install @@ -197,13 +202,14 @@ f and quote everything). In addition remove unused scripts and examples from you (e.g. everything in example/) and disable/remove all unused bot commands. It's important to escape or remove $ in input from user, files or network (as bashbot does). One of the powerful features of Unix shells is variable and command substitution -using ${} and$() can lead to remote code execution (RCE) or remote information disclosure +using ${} and $() can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped $ is included in untrusted input (e.g. $$ or $(rm -rf /*)). A powerful tool to improve your scripts is shellcheck. You can use it online [https:// -www.shellcheck.net/] or install_shellcheck_locally. Shellcheck is used extensively in -bashbot development to ensure a high code quality (e.g. it's not allowed to push changes -without passing all shellcheck tests). In addition bashbot has a test_suite to check if -important functionality is working as expected. +www.shellcheck.net/] or install shellcheck locally [https://github.com/koalaman/ +shellcheck#installing]. Shellcheck is used extensively in bashbot development to ensure a +high code quality (e.g. it's not allowed to push changes without passing all shellcheck +tests). In addition bashbot has a test suite [doc/7_develop.md] to check if important +functionality is working as expected. Use printf whenever possible @@ -226,11 +232,14 @@ Secure your Bot installation Your Bot configuration must not be readable by other users. Everyone who can read your Bots token is able to act as your Bot and has access to all chats the Bot is in! Everyone with read access to your Bot files can extract your Bots data. Especially your -Bot config inconfig.jssh must be protected against other users. No one except you should -have write access to the Bot files. The Bot should be restricted to have write access -tocount.jssh and data-bot-bash only, all other files must be write protected. -To set access rights for your bashbot installation to a reasonable default runsudo ./ +Bot config in config.jssh must be protected against other users. No one except you should +have write access to the Bot files. The Bot should be restricted to have write access to +count.jssh, data-bot-bash/ and logs/ only, all other files must be write protected. +To set access rights for your bashbot installation to a reasonable default run sudo ./ bashbot.sh init after every update or change to your installation directory. +Note: Keep old log files in a safe place or even better delete them, they are GDPR +relevant and may contain information [https://github.com/topkecleon/telegram-bot-bash/ +issues/174] you don't want to be public. FAQ @@ -258,9 +267,9 @@ Nevertheless there are more reasons from my side: Can I have the single bashbot.sh file back? -At the beginning bashbot was simply the filebashbot.sh that you could copy everywhere and +At the beginning bashbot was simply the file bashbot.sh that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more. -Hey no problem, if you are finished with your cool bot, rundev/make-standalone.sh to +Hey no problem, if you are finished with your cool bot, run dev/make-standalone.sh to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see Create a stripped down version of your Bot [doc/7_develop.md]. @@ -309,5 +318,5 @@ That's it all guys! If you feel that there's something missing or if you found a bug, feel free to submit a pull request! -$$VERSION$$ v1.30-0-g3266427 +$$VERSION$$ v1.40-dev-34-g1440d56 diff --git a/addons/antiFlood.sh b/addons/antiFlood.sh index b4012a3..1fd6ef8 100644 --- a/addons/antiFlood.sh +++ b/addons/antiFlood.sh @@ -4,7 +4,7 @@ # this addon counts how many files, e.g. stickers, are sent to # a chat and takes actions if threshold is reached # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # used events: # diff --git a/addons/example.sh b/addons/example.sh index 87a8caf..15cd539 100644 --- a/addons/example.sh +++ b/addons/example.sh @@ -4,7 +4,7 @@ # Addons can register to bashbot events at startup # by providing their name and a callback per event # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # # If an event occurs each registered event function is called. # diff --git a/bashbot.rc b/bashbot.rc index 9e51c8c..0ff801d 100755 --- a/bashbot.rc +++ b/bashbot.rc @@ -5,7 +5,7 @@ # # tested on: ubuntu, opensuse, debian # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # shellcheck disable=SC2009 # shellcheck disable=SC2181 diff --git a/bashbot.sh b/bashbot.sh index a3a9958..404fad6 100755 --- a/bashbot.sh +++ b/bashbot.sh @@ -30,16 +30,9 @@ BOTCOMMANDS="-h help init start stop status suspendback resumeback killb # 8 - curl/wget missing # 10 - not bash! # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ################################################################## -# 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 10 -fi - # are we running in a terminal? NN="\n" if [ -t 1 ] && [ -n "${TERM}" ]; then @@ -51,6 +44,7 @@ if [ -t 1 ] && [ -n "${TERM}" ]; then NC='\e[0m' NN="${NC}\n" fi +declare -r INTERACTIVE RED GREEN ORANGE GREY NC NN # telegram uses utf-8 characters, check if we have an utf-8 charset if [ "${LANG}" = "${LANG%[Uu][Tt][Ff]*}" ]; then @@ -89,7 +83,8 @@ _is_function() { # if $2 is not given or is not a positive number zero is assumed _round_float() { local digit="$2"; [[ "$2" =~ ^[${o9o9o9}]+$ ]] || digit="0" - { LC_ALL=C.utf-8 printf "%.${digit}f" "$1"; } 2>/dev/null + : "$(LC_ALL=C printf "%.${digit}f" "$1" 2>/dev/null)" + printf "%s" "${_//,/.}" # make more LANG independent } # date is external, printf is much faster _date(){ @@ -104,6 +99,12 @@ getConfigKey() { [[ "$1" =~ ^[-${azAZo9},._]+$ ]] || return 3 [ -r "${BOTCONFIG}.jssh" ] && sed -n 's/\["'"$1"'"\]\t*"\(.*\)"/\1/p' "${BOTCONFIG}.jssh" | tail -n 1 } +# escape / remove text characters for json strings, eg. " -> \" +# $1 string +# output escaped string +JsonEscape(){ + sed 's/\([-"`´,§$%&/(){}#@!?*.\t]\)/\\\1/g' <<< "$1" +} # check if $1 seems a valid token # return true if token seems to be valid check_token(){ @@ -172,9 +173,21 @@ RUNDIR="$(dirname "$0")" MODULEDIR="${SCRIPTDIR}/modules" -# adjust locations based on source and real name -[[ "${SCRIPT}" != "${REALME}" || "$1" == "source" ]] && SOURCE="yes" +# adjust stuff for source, use return from source without source +alias exit_source='exit' +if [[ "${SCRIPT}" != "${REALME}" || "$1" == "source" ]]; then + SOURCE="yes" + [ -z "$1" ] && alias exit_source='printf "Exit from source ...\n";return' +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 + +# adjust path variables if [ -n "${BASHBOT_HOME}" ]; then SCRIPTDIR="${BASHBOT_HOME}" else @@ -184,7 +197,7 @@ fi [ -z "${BASHBOT_VAR}" ] && BASHBOT_VAR="${BASHBOT_HOME}" ADDONDIR="${BASHBOT_ETC:-.}/addons" -RUNUSER="${USER}" # USER is overwritten by bashbot array :-(, save original +RUNUSER="${USER}" # save original USER # provide help case "$1" in @@ -207,16 +220,18 @@ esac # OK, ENVIRONMENT is set up, let's do some additional tests if [[ -z "${SOURCE}" && -z "${BASHBOT_HOME}" ]] && ! cd "${RUNDIR}" ; then printf "${RED}ERROR: Can't change to ${RUNDIR} ...${NN}" - exit 1 + exit_source 1 fi RUNDIR="." [ ! -w "." ] && printf "${ORANGE}WARNING: ${RUNDIR} is not writeable!${NN}" # check if JSON.sh is available JSONSHFILE="${BASHBOT_JSONSH:-${SCRIPTDIR}/JSON.sh/JSON.sh}" -[ ! -x "${JSONSHFILE}" ] &&\ - 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 3 +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 # file locations based on ENVIRONMENT BOTCONFIG="${BASHBOT_ETC:-.}/botconfig" @@ -264,7 +279,7 @@ if [ -z "${BOTTOKEN}" ]; then # check data dir file if [ ! -w "${DATADIR}" ]; then printf "${RED}ERROR: ${DATADIR} does not exist or is not writeable!.${NN}" - exit 2 + exit_source 2 fi # setup count file if [ ! -f "${COUNTFILE}.jssh" ]; then @@ -272,7 +287,7 @@ if [ -z "${BOTTOKEN}" ]; then elif [ ! -w "${COUNTFILE}.jssh" ]; then printf "${RED}ERROR: Can't write to ${COUNTFILE}!.${NN}" ls -l "${COUNTFILE}.jssh" - exit 2 + exit_source 2 fi # setup blocked file if [ ! -f "${BLOCKEDFILE}.jssh" ]; then @@ -301,7 +316,7 @@ if [ -z "${BOTTOKEN}" ]; then BOTTOKEN="$(getConfigKey "bottoken")" else printf "\n${RED}Error: Can't recover from missing bot token! Remove ${BOTCONFIG}.jssh and run${NC} bashbot.sh init\n" - exit 7 + exit_source 7 fi fi fi @@ -324,7 +339,7 @@ fi ################## # here we start with the real stuff -BASHBOT_RETRY="" # retry by default +BASHBOT_RETRY="" # retry by default URL="${BASHBOT_URL:-https://api.telegram.org/bot}${BOTTOKEN}" ME_URL=${URL}'/getMe' @@ -336,11 +351,18 @@ declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR BOTACL DATADIR COUNTFILE declare -rx BOTTOKEN URL ME_URL declare -ax CMD -declare -Ax UPD BOTSENT USER MESSAGE URLS CONTACT LOCATION CHAT FORWARD REPLYTO VENUE iQUERY +declare -Ax UPD BOTSENT USER MESSAGE URLS CONTACT LOCATION CHAT FORWARD REPLYTO VENUE iQUERY iBUTTON declare -Ax SERVICE NEWMEMBER LEFTMEMBER PINNED MIGRATE export res CAPTION ME +############### +# 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 + ################## # read commands file if we are not sourced COMMANDS="${BASHBOT_ETC:-.}/commands.sh" @@ -350,14 +372,9 @@ if [ -r "${COMMANDS}" ]; then else [ -z "${SOURCE}" ] && printf "${RED}Warning: ${COMMANDS} does not exist or is not readable!.${NN}" fi -debug_checks "start SOURCE=${SOURCE:-no}" "$@" +# no debug checks on source +[ -z "${SOURCE}" ] && debug_checks "start" "$@" -############### -# 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 ##################### # BASHBOT INTERNAL functions @@ -368,7 +385,7 @@ sed '1ia' /dev/null || printf "${ORANGE}Warning: You may run on a B #jsonDB is now mandatory if ! _is_function jssh_newDB; then printf "${RED}ERROR: Mandatory module jsonDB is missing or not readable!${NN}" - exit 6 + exit_source 6 fi # $1 URL, $2 filename in DATADIR @@ -416,10 +433,11 @@ delete_message() { sendJson "$1" '"message_id": '"$2"'' "${DELETE_URL}" } +# get download url for file id, $1 file_id get_file() { [ -z "$1" ] && return sendJson "" '"file_id": "'"$1"'"' "${URL}/getFile" - printf '%s\n' "${URL}"/"$(JsonGetString <<< "${res}" '"result","file_path"')" + printf "%s\n" "${URL}/${UPD["result,file_path"]}" } # iconv used to filter out broken utf characters, if not installed fake it @@ -436,13 +454,14 @@ sendJson(){ local json chat="" if [ -n "$1" ]; then chat='"chat_id":'"$1"',' - [[ "$1" == *[!${o9o9o9}-]* ]] && chat='"chat_id":"'"$1"' NAN",' # chat id not a number! + [[ "$1" == *[!${o9o9o9}-]* ]] && chat='"chat_id":"'"$1"' NAN",' # chat id not a number! fi # compose final json json='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<"$2")"'}' if [ -n "${BASHBOTDEBUG}" ] ; then log_update "sendJson (${DETECTED_CURL}) CHAT=${chat#*:} JSON=${2:0:100} URL=${3##*/}" - log_message "DEBUG sendJson ==========\n$("${JSONSHFILE}" -b -n <<<"${json}" 2>&1)" + # mask " and \ , remove newline from json + log_message "DEBUG sendJson ==========\n$("${JSONSHFILE}" -b -n <<<"$(sed -E -e 's/\\"/+/g' -e 's/\\/\\\\/g' -e 's/(\r|\n)//g' <<<"${json}")" 2>&1)" fi # chat id not a number if [[ "${chat}" == *"NAN\"," ]]; then @@ -458,6 +477,7 @@ sendJson(){ [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "send" "${@}" & } + # # curl / wget specific functions # @@ -519,7 +539,7 @@ else else printf "${RED}Error: curl and wget not found, install curl!${NN}" fi - exit 8 + exit_source 8 fi fi @@ -557,8 +577,8 @@ sendJsonResult(){ [ -n "${BASHBOTDEBUG}" ] && log_message "New Result ==========\n$1" BOTSENT[OK]="${UPD["ok"]}" if [ "${BOTSENT[OK]}" = "true" ]; then - BOTSENT[ID]="${UPD["result","message_id"]}" - BOTSENT[CHAT]="${UPD["result","chat","id"]}" + BOTSENT[ID]="${UPD["result,message_id"]}" + BOTSENT[CHAT]="${UPD["result,chat,id"]}" [ -n "${UPD["result"]}" ] && BOTSENT[RESULT]="${UPD["result"]}" return # hot path everything OK! @@ -567,7 +587,7 @@ sendJsonResult(){ if [ -n "$1" ]; then BOTSENT[ERROR]="${UPD["error_code"]}" BOTSENT[DESCRIPTION]="${UPD["description"]}" - [ -n "${UPD["parameters","retry_after"]}" ] && BOTSENT[RETRY]="${UPD["parameters","retry_after"]}" + [ -n "${UPD["parameters,retry_after"]}" ] && BOTSENT[RETRY]="${UPD["parameters,retry_after"]}" else BOTSENT[OK]="false" BOTSENT[ERROR]="999" @@ -616,13 +636,6 @@ sendJsonResult(){ fi } >>"${ERRORLOG}" -# escape / remove text characters for json strings, eg. " -> \" -# $1 string -# output escaped string -JsonEscape(){ - sed 's/\([-"`´,§$%&/(){}#@!?*.\t]\)/\\\1/g' <<< "$1" -} - # convert common telegram entities to JSON # title caption description markup inlinekeyboard title2Json(){ @@ -659,83 +672,9 @@ JsonDecode() { printf "%b\n" "${out}${remain}" } -JsonGetString() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]"\(.*\)"$/\1/p' -} -JsonGetLine() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]//p' -} -JsonGetValue() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]\([0-9.,]*\).*/\1/p' -} - -################ -# processing of updates starts here -process_updates() { - local max num debug="$1" - max="$(grep -F ',"update_id"]' <<< "${UPDATE}" | tail -1 | cut -d , -f 2 )" - Json2Array 'UPD' <<<"${UPDATE}" - for ((num=0; num<=max; num++)); do - process_client "${num}" "${debug}" - done -} - -process_client() { - local num="$1" debug="$2" - pre_process_message "${num}" - # log message on debug - [[ -n "${debug}" ]] && log_message "New Message ==========\n$(grep -F '["result",'"${num}" <<<"${UPDATE}")" - - # check for users / groups to ignore - jssh_updateArray_async "BASHBOTBLOCKED" "${BLOCKEDFILE}" - [ -n "${USER[ID]}" ] && [[ -n "${BASHBOTBLOCKED[${USER[ID]}]}" || -n "${BASHBOTBLOCKED[${CHAT[ID]}]}" ]] && return - - # process per message type - if [ -z "${iQUERY[ID]}" ]; then - if grep -qs -e '\["result",'"${num}"',"edited_message"' <<<"${UPDATE}"; then - # edited message - UPDATE="${UPDATE//,${num},\"edited_message\",/,${num},\"message\",}" - Json2Array 'UPD' <<<"${UPDATE}" - MESSAGE[0]="/_edited_message " - fi - process_message "${num}" "${debug}" - printf "%(%c)T: update received FROM=%s CHAT=%s CMD=%s\n" -1 "${USER[USERNAME]:0:20} (${USER[ID]})"\ - "${CHAT[USERNAME]:0:20}${CHAT[TITLE]:0:30} (${CHAT[ID]})"\ - "${MESSAGE:0:30}${CAPTION:0:30}${URLS[*]:0:30}" >>"${UPDATELOG}" - else - process_inline "${num}" "${debug}" - printf "%(%c)T: iQuery received FROM=%s iQUERY=%s\n" -1\ - "${iQUERY[USERNAME]:0:20} (${iQUERY[USER_ID]})" "${iQUERY[0]}" >>"${UPDATELOG}" - fi - ##### - # process inline and message events - # first classic command dispatcher - # shellcheck source=./commands.sh - source "${COMMANDS}" "${debug}" & - - # then all registered addons - if [ -z "${iQUERY[ID]}" ]; then - event_message "${debug}" - else - event_inline "${debug}" - fi - - # last count users - jssh_countKeyDB_async "${CHAT[ID]}" "${COUNTFILE}" -} - -declare -Ax BASHBOT_EVENT_INLINE BASHBOT_EVENT_MESSAGE BASHBOT_EVENT_CMD BASHBOT_EVENT_REPLYTO BASHBOT_EVENT_FORWARD BASHBOT_EVENT_SEND -declare -Ax BASHBOT_EVENT_CONTACT BASHBOT_EVENT_LOCATION BASHBOT_EVENT_FILE BASHBOT_EVENT_TEXT BASHBOT_EVENT_TIMER BASHBOT_BLOCKED - -start_timer(){ - # send alarm every ~60 s - while :; do - sleep 59.5 - kill -ALRM $$ - done; -} EVENT_SEND="0" +declare -Ax BASHBOT_EVENT_SEND event_send() { # max recursion level 5 to avoid fork bombs (( EVENT_SEND++ )); [ "${EVENT_SEND}" -gt "5" ] && return @@ -746,425 +685,36 @@ event_send() { done } -EVENT_TIMER="0" -event_timer() { - local key timer debug="$1" - (( EVENT_TIMER++ )) - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_TIMER[@]}" - do - timer="${key##*,}" - [[ ! "${timer}" =~ ^-*[1-9][0-9]*$ ]] && continue - if [ "$(( EVENT_TIMER % timer ))" = "0" ]; then - _exec_if_function "${BASHBOT_EVENT_TIMER[${key}]}" "timer" "${key}" "${debug}" - [ "$(( EVENT_TIMER % timer ))" -lt "0" ] && \ - unset BASHBOT_EVENT_TIMER["${key}"] - fi - done -} - -event_inline() { - local key debug="$1" - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_INLINE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_INLINE[${key}]}" "inline" "${key}" "${debug}" - done -} -event_message() { - local key debug="$1" - # ${MESSAEG[*]} event_message - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_MESSAGE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_MESSAGE[${key}]}" "message" "${key}" "${debug}" - done - - # ${TEXT[*]} event_text - if [ -n "${MESSAGE[0]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_TEXT[@]}" - do - _exec_if_function "${BASHBOT_EVENT_TEXT[${key}]}" "text" "${key}" "${debug}" - done - - # ${CMD[*]} event_cmd - if [ -n "${CMD[0]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_CMD[@]}" - do - _exec_if_function "${BASHBOT_EVENT_CMD[${key}]}" "command" "${key}" "${debug}" - done - fi - fi - # ${REPLYTO[*]} event_replyto - if [ -n "${REPLYTO[UID]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_REPLYTO[@]}" - do - _exec_if_function "${BASHBOT_EVENT_REPLYTO[${key}]}" "replyto" "${key}" "${debug}" - done - fi - - # ${FORWARD[*]} event_forward - if [ -n "${FORWARD[UID]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_FORWARD[@]}" - do - _exec_if_function && "${BASHBOT_EVENT_FORWARD[${key}]}" "forward" "${key}" "${debug}" - done - fi - - # ${CONTACT[*]} event_contact - if [ -n "${CONTACT[FIRST_NAME]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_CONTACT[@]}" - do - _exec_if_function "${BASHBOT_EVENT_CONTACT[${key}]}" "contact" "${key}" "${debug}" - done - fi - - # ${VENUE[*]} event_location - # ${LOCATION[*]} event_location - if [ -n "${LOCATION[LONGITUDE]}" ] || [ -n "${VENUE[TITLE]}" ]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_LOCATION[@]}" - do - _exec_if_function "${BASHBOT_EVENT_LOCATION[${key}]}" "location" "${key}" "${debug}" - done - fi - - # ${URLS[*]} event_file - # NOTE: compare again #URLS -1 blanks! - if [[ "${URLS[*]}" != " " ]]; then - # shellcheck disable=SC2153 - for key in "${!BASHBOT_EVENT_FILE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_FILE[${key}]}" "file" "${key}" "${debug}" - done - fi - -} -pre_process_message(){ - local num="$1" - # unset everything to not have old values - CMD=( ); iQUERY=( ); MESSAGE=(); CHAT=(); USER=(); CONTACT=(); LOCATION=(); unset CAPTION - REPLYTO=( ); FORWARD=( ); URLS=(); VENUE=( ); SERVICE=( ); NEWMEMBER=( ); LEFTMEMBER=( ); PINNED=( ); MIGRATE=( ) - iQUERY[ID]="${UPD["result",${num},"inline_query","id"]}" - CHAT[ID]="${UPD["result",${num},"message","chat","id"]}" - USER[ID]="${UPD["result",${num},"message","from","id"]}" - [ -z "${CHAT[ID]}" ] && CHAT[ID]="${UPD["result",${num},"edited_message","chat","id"]}" - [ -z "${USER[ID]}" ] && USER[ID]="${UPD["result",${num},"edited_message","from","id"]}" - # always true - return 0 -} -process_inline() { - local num="$1" - iQUERY[0]="$(JsonDecode "${UPD["result",${num},"inline_query","query"]}")" - iQUERY[USER_ID]="${UPD["result",${num},"inline_query","from","id"]}" - iQUERY[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","first_name"]}")" - iQUERY[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","last_name"]}")" - iQUERY[USERNAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","username"]}")" - # always true - return 0 -} -process_message() { - local num="$1" - # Message - MESSAGE[0]+="$(JsonDecode "${UPD["result",${num},"message","text"]}" | sed 's|\\/|/|g')" - MESSAGE[ID]="${UPD["result",${num},"message","message_id"]}" - - # Chat ID is now parsed when update is received - CHAT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","last_name"]}")" - CHAT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","first_name"]}")" - CHAT[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","chat","username"]}")" - # set real name as username if empty - [ -z "${CHAT[USERNAME]}" ] && CHAT[USERNAME]="${CHAT[FIRST_NAME]} ${CHAT[LAST_NAME]}" - CHAT[TITLE]="$(JsonDecode "${UPD["result",${num},"message","chat","title"]}")" - CHAT[TYPE]="$(JsonDecode "${UPD["result",${num},"message","chat","type"]}")" - CHAT[ALL_ADMIN]="${UPD["result",${num},"message","chat","all_members_are_administrators"]}" - - # user ID is now parsed when update is received - #USER[ID]="${UPD["result",${num},"message","from","id"]}" - USER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","first_name"]}")" - USER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","last_name"]}")" - USER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","from","username"]}")" - # set real name as username if empty - [ -z "${USER[USERNAME]}" ] && USER[USERNAME]="${USER[FIRST_NAME]} ${USER[LAST_NAME]}" - - # in reply to message from - if [ -n "${UPD["result",${num},"message","reply_to_message","from","id"]}" ]; then - REPLYTO[UID]="${UPD["result",${num},"message","reply_to_message","from","id"]}" - REPLYTO[0]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","text"]}")" - REPLYTO[ID]="${UPD["result",${num},"message","reply_to_message","message_id"]}" - REPLYTO[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","first_name"]}")" - REPLYTO[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","last_name"]}")" - REPLYTO[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","username"]}")" - fi - - # forwarded message from - if [ -n "${UPD["result",${num},"message","forward_from","id"]}" ]; then - FORWARD[UID]="${UPD["result",${num},"message","forward_from","id"]}" - FORWARD[ID]="${MESSAGE[ID]}" # same as message ID - FORWARD[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","first_name"]}")" - FORWARD[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","last_name"]}")" - FORWARD[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","username"]}")" - fi - - # get file URL from telegram, check for any of them! - if grep -qs -e '\["result",'"${num}"',"message","[avpsd].*,"file_id"\]' <<<"${UPDATE}"; then - URLS[AUDIO]="$(get_file "${UPD["result",${num},"message","audio","file_id"]}")" - URLS[DOCUMENT]="$(get_file "${UPD["result",${num},"message","document","file_id"]}")" - URLS[PHOTO]="$(get_file "${UPD["result",${num},"message","photo",0,"file_id"]}")" - URLS[STICKER]="$(get_file "${UPD["result",${num},"message","sticker","file_id"]}")" - URLS[VIDEO]="$(get_file "${UPD["result",${num},"message","video","file_id"]}")" - URLS[VOICE]="$(get_file "${UPD["result",${num},"message","voice","file_id"]}")" - fi - # Contact, must have phone_number - if [ -n "${UPD["result",${num},"message","contact","phone_number"]}" ]; then - CONTACT[USER_ID]="$(JsonDecode "${UPD["result",${num},"message","contact","user_id"]}")" - CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","first_name"]}")" - CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","last_name"]}")" - CONTACT[NUMBER]="${UPD["result",${num},"message","contact","phone_number"]}" - CONTACT[VCARD]="$(JsonGetString '"result",'"${num}"',"message","contact","vcard"' <<<"${UPDATE}")" - fi - - # venue, must have a position - if [ -n "${UPD["result",${num},"message","venue","location","longitude"]}" ]; then - VENUE[TITLE]="$(JsonDecode "${UPD["result",${num},"message","venue","title"]}")" - VENUE[ADDRESS]="$(JsonDecode "${UPD["result",${num},"message","venue","address"]}")" - VENUE[LONGITUDE]="${UPD["result",${num},"message","venue","location","longitude"]}" - VENUE[LATITUDE]="${UPD["result",${num},"message","venue","location","latitude"]}" - VENUE[FOURSQUARE]="${UPD["result",${num},"message","venue","foursquare_id"]}" - fi - - # Caption - CAPTION="$(JsonDecode "${UPD["result",${num},"message","caption"]}")" - - # Location - LOCATION[LONGITUDE]="${UPD["result",${num},"message","location","longitude"]}" - LOCATION[LATITUDE]="${UPD["result",${num},"message","location","latitude"]}" - - # service messages, group or channel only! - if [[ "${CHAT[ID]}" == "-"* ]] ; then - # new chat member - if [ -n "${UPD["result",${num},"message","new_chat_member","id"]}" ]; then - SERVICE[NEWMEMBER]="${UPD["result",${num},"message","new_chat_member","id"]}" - NEWMEMBER[ID]="${SERVICE[NEWMEMBER]}" - NEWMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","first_name"]}")" - NEWMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","last_name"]}")" - NEWMEMBER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","username"]}")" - NEWMEMBER[ISBOT]="${UPD["result",${num},"message","new_chat_member","is_bot"]}" - [ -z "${MESSAGE[0]}" ] &&\ - MESSAGE[0]="/_new_chat_member ${NEWMEMBER[ID]} ${NEWMEMBER[USERNAME]:=${NEWMEMBER[FIRST_NAME]} ${NEWMEMBER[LAST_NAME]}}" - fi - # left chat member - if [ -n "${UPD["result",${num},"message","left_chat_member","id"]}" ]; then - SERVICE[LEFTMEMBER]="${UPD["result",${num},"message","left_chat_member","id"]}" - LEFTMEMBER[ID]="${SERVICE[LEFTMEBER]}" - LEFTMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","first_name"]}")" - LEFTMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","last_name"]}")" - LEFTMEBER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","username"]}")" - LEFTMEMBER[ISBOT]="${UPD["result",${num},"message","left_chat_member","is_bot"]}" - [ -z "${MESSAGE[0]}" ] &&\ - MESSAGE[0]="/_left_chat_member ${LEFTMEMBER[ID]} ${LEFTMEMBER[USERNAME]:=${LEFTMEMBER[FIRST_NAME]} ${LEFTMEMBER[LAST_NAME]}}" - fi - # chat title / photo, check for any of them! - if grep -qs -e '\["result",'"${num}"',"message","new_chat_[tp]' <<<"${UPDATE}"; then - SERVICE[NEWTITLE]="$(JsonDecode "${UPD["result",${num},"message","new_chat_title"]}")" - [ -z "${MESSAGE[0]}" ] && [ -n "${SERVICE[NEWTITLE]}" ] &&\ - MESSAGE[0]="/_new_chat_title ${USER[ID]} ${SERVICE[NEWTITLE]}" - SERVICE[NEWPHOTO]="$(get_file "${UPD["result",${num},"message","new_chat_photo",0,"file_id"]}")" - [ -z "${MESSAGE[0]}" ] && [ -n "${SERVICE[NEWPHOTO]}" ] &&\ - MESSAGE[0]="/_new_chat_photo ${USER[ID]} ${SERVICE[NEWPHOTO]}" - fi - # pinned message - if [ -n "${UPD["result",${num},"message","pinned_message","message_id"]}" ]; then - SERVICE[PINNED]="${UPD["result",${num},"message","pinned_message","message_id"]}" - PINNED[ID]="${SERVICE[PINNED]}" - PINNED[MESSAGE]="$(JsonDecode "${UPD["result",${num},"message","pinned_message","text"]}")" - [ -z "${MESSAGE[0]}" ] &&\ - MESSAGE[0]="/_new_pinned_message ${USER[ID]} ${PINNED[ID]} ${PINNED[MESSAGE]}" - fi - # migrate to super group - if [ -n "${UPD["result",${num},"message","migrate_to_chat_id"]}" ]; then - MIGRATE[TO]="${UPD["result",${num},"message","migrate_to_chat_id"]}" - MIGRATE[FROM]="${UPD["result",${num},"message","migrate_from_chat_id"]}" - SERVICE[MIGRATE]="${MIGRATE[FROM]} ${MIGRATE[TO]}" - [ -z "${MESSAGE[0]}" ] &&\ - MESSAGE[0]="/_migrate_group ${SERVICE[MIGRATE]}" - fi - # set SERVICE to yes if a service message was received - [[ "${SERVICE[*]}" =~ ^[[:blank:]]*$ ]] || SERVICE[0]="yes" - fi - - # split message in command and args - [[ "${MESSAGE[0]}" == "/"* ]] && read -r CMD <<<"${MESSAGE[0]}" && CMD[0]="${CMD[0]%%@*}" - # everything went well - return 0 -} - -######################### -# main get updates loop, should never terminate -declare -A BASHBOTBLOCKED -start_bot() { - local DEBUGMSG OFFSET=0 - # adaptive sleep defaults - local nextsleep="100" - local stepsleep="${BASHBOT_SLEEP_STEP:-100}" - local maxsleep="${BASHBOT_SLEEP:-5000}" - # startup message - DEBUGMSG="Start BASHBOT updates in Mode \"${1:-normal}\" ==========" - log_update "${DEBUGMSG}" - # redirect to Debug.log - [[ "$1" == *"debug" ]] && exec &>>"${DEBUGLOG}" - log_debug "${DEBUGMSG}"; DEBUGMSG="$1" - [[ "${DEBUGMSG}" == "xdebug"* ]] && set -x - # cleaup old pipes and empty logfiles - find "${DATADIR}" -type p -delete - find "${DATADIR}" -size 0 -name "*.log" -delete - # load addons on startup - for addons in "${ADDONDIR:-.}"/*.sh ; do - # shellcheck source=./modules/aliases.sh - [ -r "${addons}" ] && source "${addons}" "startbot" "${DEBUGMSG}" - done - # shellcheck source=./commands.sh - source "${COMMANDS}" "startbot" - # start timer events - if [ -n "${BASHBOT_START_TIMER}" ] ; then - # shellcheck disable=SC2064 - trap "event_timer ${DEBUGMSG}" ALRM - start_timer & - # shellcheck disable=SC2064 - trap "kill -9 $!; exit" EXIT INT HUP TERM QUIT - fi - # cleanup countfile on startup - jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" "${COUNTFILE}" - [ -f "${COUNTFILE}.jssh.flock" ] && rm -f "${COUNTFILE}.jssh.flock" - # store start time and cleanup botconfig on startup - jssh_updateKeyDB "startup" "$(_date)" "${BOTCONFIG}" - [ -f "${BOTCONFIG}.jssh.flock" ] && rm -f "${BOTCONFIG}.jssh.flock" - # read blocked users - jssh_readDB_async "BASHBOTBLOCKED" "${BLOCKEDFILE}" - # inform botadmin about start - send_normal_message "$(getConfigKey "botadmin")" "Bot $(getConfigKey "botname") started ..." & - ########## - # bot is ready, start processing updates ... - while true; do - # adaptive sleep in ms rounded to next 0.1 s - sleep "$(_round_float "${nextsleep}e-3" "1")" - # get next update - UPDATE="$(getJson "${URL}/getUpdates?offset=${OFFSET}" 2>/dev/null | "${JSONSHFILE}" -b -n 2>/dev/null | iconv -f utf-8 -t utf-8 -c)" - # did we get an response? - if [ -n "${UPDATE}" ]; then - # we got something, do processing - [ "${OFFSET}" = "-999" ] && [ "${nextsleep}" -gt "$((maxsleep*2))" ] &&\ - log_error "Recovered from timeout/broken/no connection, continue with telegram updates" - # escape bash $ expansion bug - ((nextsleep+= stepsleep , nextsleep= nextsleep>maxsleep ?maxsleep:nextsleep)) - UPDATE="${UPDATE//$/\\$}" - # Offset - OFFSET="$(grep <<< "${UPDATE}" '\["result",[0-9]*,"update_id"\]' | tail -1 | cut -f 2)" - ((OFFSET++)) - - if [ "${OFFSET}" != "1" ]; then - nextsleep="100" - process_updates "${DEBUGMSG}" - fi - else - # oops, something bad happened, wait maxsleep*10 - (( nextsleep=nextsleep*2 , nextsleep= nextsleep>maxsleep*10 ?maxsleep*10:nextsleep )) - # second time, report problem - if [ "${OFFSET}" = "-999" ]; then - log_error "Repeated timeout/broken/no connection on telegram update, sleep $(_round_float "${nextsleep}e-3")s" - # try to recover - if _is_function bashbotBlockRecover && [ -z "$(getJson "${ME_URL}")" ]; then - log_error "Try to recover, calling bashbotBlockRecover ..." - bashbotBlockRecover >>"${ERRORLOG}" - fi - fi - OFFSET="-999" - fi - done -} +# fallback version, full version is in bin/bashbot_init.in.sh # initialize bot environment, user and permissions bot_init() { - [ -n "${BASHBOT_HOME}" ] && cd "${BASHBOT_HOME}" || exit 1 - local runuser chown touser botname DEBUG="$1" - # upgrade from old version - # currently no action - printf "Check for Update actions ...\n" - printf "Done.\n" - # load addons on startup - printf "Initialize modules and addons ...\n" + if [ -n "${BASHBOT_HOME}" ] && ! cd "${BASHBOT_HOME}"; then + printf "Can't change to BASHBOT_HOME" + exit 1 + fi + # initialize addons + printf "Initialize addons ...\n" for addons in "${ADDONDIR:-.}"/*.sh ; do # shellcheck source=./modules/aliases.sh - [ -r "${addons}" ] && source "${addons}" "init" "${DEBUG}" + [ -r "${addons}" ] && source "${addons}" "init" "$1" done printf "Done.\n" - # ask for bashbot user - runuser="${RUNUSER}"; [ "${UID}" = "0" ] && runuser="nobody" - printf "Enter User to run bashbot [${runuser}]: " - read -r chown - [ -z "${chown}" ] && chown="${runuser}"; touser="${chown%:*}" - # check user ... - if ! id "${touser}" &>/dev/null; then - printf "${RED}User \"${touser}\" does not exist!${NN}" - exit 3 - elif [[ "${UID}" != "0" && "${touser}" != "${runuser}" ]]; then - # different user but not root ... - printf "${ORANGE}You are not root, adjusting permissions may fail. Try \"sudo ./bashbot.sh init\"${NN}Press to stop or to continue..." 1>&2 - [ -n "${INTERACTIVE}" ] && read -r runuser - fi # adjust permissions - printf "Adjusting files and permissions for user \"${touser}\" ...\n" + printf "Adjusting files and permissions ...\n" 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 \{\} + - chown -Rf "${chown}" . ./* printf "Done.\n" - # adjust values in bashbot.rc - if [ -w "bashbot.rc" ]; then - printf "Adjust user and botname in bashbot.rc ...\n" - sed -i '/^[# ]*runas=/ s|runas=.*$|runas="'"${touser}"'"|' "bashbot.rc" - sed -i '/^[# ]*bashbot=/ s|bashbot=.*$|bashbot="cd '"${PWD}"'; '"${PWD}"'/'"${0##*/}"'"|' "bashbot.rc" - botname="$(getConfigKey "botname")" - [ -n "${botname}" ] && sed -i '/^[# ]*name=/ s|name=.*$|name="'"${botname}"'"|' "bashbot.rc" - printf "Done.\n" - fi - # ask to check bottoken online - if [ -z "$(getConfigKey "botid")" ]; then - printf "Seems to be your first init. Should I verify your bot token online? (y/N) N\b" - read -r ANSWER - if [[ "${ANSWER}" =~ ^[Yy] ]]; then - printf "${GREEN}Contacting telegram to verify your bot token ...${NN}" - $0 botname - fi - fi - # check if botconf seems valid - printf "${GREEN}This is your bot config:${NN}" - sed 's/^/\t/' "${BOTCONFIG}.jssh" | grep -vF '["bot_config_key"]' - if check_token "$(getConfigKey "bottoken")" && [[ "$(getConfigKey "botadmin")" =~ ^[${o9o9o9}]+$ ]]; then - printf "Bot config seems to be valid. Should I make a backup copy? (Y/n) Y\b" - read -r ANSWER - if [[ -z "${ANSWER}" || "${ANSWER}" =~ ^[^Nn] ]]; then - printf "Copy bot config to ${BOTCONFIG}.jssh.ok ...\n" - cp "${BOTCONFIG}.jssh" "${BOTCONFIG}.jssh.ok" - fi - else - printf "${ORANGE}Bot config may incomplete, pls check.${NN}" - fi - # show result - ls -ld "${DATADIR}" "${LOGDIR}" ./*.jssh* ./*.sh 2>/dev/null + _exec_if_function my_init } if ! _is_function send_message ; then printf "${RED}ERROR: send_message is not available, did you deactivate ${MODULEDIR}/sendMessage.sh?${NN}" - exit 1 + exit_source 1 fi # check if JSON.awk exist and has x flag @@ -1201,7 +751,7 @@ if [ -z "${SOURCE}" ]; then [ "$1" = "botname" ] && exit ;;& # used to send output of background and interactive to chats - "outproc") # $2 chat_id $3 identifier of job, internal use only! + "outproc") # $2 chat_id $3 identifier of job, internal use only! [ -z "$3" ] && printf "No job identifier\n" && exit 3 [ -z "$2" ] && printf "No chat to send to\n" && exit 3 ME="$(getConfigKey "botname")" @@ -1215,14 +765,16 @@ if [ -z "${SOURCE}" ]; then debug_checks "end outproc" "$@" exit ;; - # finally starts the read update loop, internal use only1 + # finally starts the read update loop, internal use only "startbot" ) - start_bot "$2" + _exec_if_function start_bot "$2" debug_checks "end startbot" "$@" exit ;; # run after every update to update files and adjust permissions "init") + # shellcheck source=./bin/bashbot._init.inc.sh" + [ -r "${BASHBOT_HOME:-.}/bin/bashbot_init.inc.sh" ] && source "${BASHBOT_HOME:-.}/bin/bashbot_init.inc.sh" bot_init "$2" debug_checks "end init" "$@" exit @@ -1256,14 +808,18 @@ if [ -z "${SOURCE}" ]; then # start bot as background job and check if bot is running "start") - # shellcheck disable=SC2086 SESSION="${ME:-_bot}-startbot" BOTPID="$(proclist "${SESSION}")" - # shellcheck disable=SC2086 - [ -n "${BOTPID}" ] && kill ${BOTPID} - nohup "${SCRIPT}" "startbot" "$2" "${SESSION}" &>/dev/null & - printf "Session Name: %s\n" "${SESSION}" - sleep 1 + if _is_function process_update; then + # shellcheck disable=SC2086 + [ -n "${BOTPID}" ] && kill ${BOTPID} && printf "${GREY}Stop already running bot ...${NN}" + 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}" + [ -n "${BOTPID}" ] && printf "${ORANGE}Already running bot found ...${NN}" + fi if [ -n "$(proclist "${SESSION}")" ]; then printf "${GREEN}Bot started successfully.${NN}" else diff --git a/bin/any_command.sh b/bin/any_command.sh new file mode 100755 index 0000000..fe8f3e7 --- /dev/null +++ b/bin/any_command.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034,SC2059 +#=============================================================================== +# +# FILE: bin/any_command.sh +# +USAGE='any_command.sh [-h|--help] [--force|--reference] bot_command args ...' +# +# DESCRIPTION: execute (almost) any bashbot command/function +# can be used for testing commands while bot development +# +# OPTIONS: -- force - execute unknown commands/functions +# by default only commands in 6_reference.md are allowed +# +# -h - display short help +# --help - this help +# +# Set BASHBOT_HOME to your installation directory +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 30.01.2021 10:24 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +COMMAND="" + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "debug" # debug +print_help "$1" + + +error="" +# check options +if [[ "$1" = "--force" ]]; then + # skip checks + shift +else + # check for --ref + ref="$1"; [[ "$1" == "--ref"* ]] && shift + if [ "${#1}" -lt 11 ];then + printf "${RED}Command must be minimum 11 characters!${NC}\n" + error=3 + fi + if [[ "$1" != *"_"* ]];then + printf "${RED}Command must contain _ (underscore)!${NC}\n" + error=3 + fi + # simple hack to get allowed commands from doc + if grep -q "^##### $1" <<<"$(sed -n -e '/^##### _is_/,$ d' -e '/^##### /p' "${BASHBOT_HOME:-..}doc/"6_*)"; then + # oiutput reference and exit + if [[ "${ref}" == "--ref"* ]]; then + sed -n -e '/^##### '"$1"'/,/^##/ p' "${BASHBOT_HOME:-..}doc/"6_* + exit + fi + else + printf "Command ${GREY}%s${NC} not found in 6_reference.md, use ${GREY}--force${NC} to execute!\n" "$1" + error=4 + fi + [ -n "${error}" ] && exit "${error}" +fi + + +#### +# ready, do stuff here ----- +COMMAND="$1" +if [ "$2" == "BOTADMIN" ]; then + ARG1="${BOT_ADMIN}" +else + ARG1="$2" +fi + +# clear result and response +BOTSENT=() +UPD=() + +# send message in selected format +"${COMMAND}" "${ARG1}" "${@:3}" + +# output result an telegram response +print_result +print_response diff --git a/bin/bashbot_env.inc.sh b/bin/bashbot_env.inc.sh index b837412..d935e64 100644 --- a/bin/bashbot_env.inc.sh +++ b/bin/bashbot_env.inc.sh @@ -13,12 +13,12 @@ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 18.12.2020 12:27 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== ############ # set where your bashbot lives -export BASHBOT_HOME BASHBOT_ETC BASHBOT_VAR FILE_REGEX +export BASHBOT_HOME BASHBOT_ETC BASHBOT_VAR FILE_REGEX ME # default: one dir up BASHBOT_HOME="$(cd "${BASH_SOURCE[0]%/*}" >/dev/null 2>&1 && pwd)/../" @@ -32,20 +32,21 @@ BASHBOT_ETC="${BASHBOT_HOME}" ##### # if files are not readable, eviroment is wrong or bashbot is not initialized -# source bashbot +# check for bashbot if [ ! -r "${BASHBOT_HOME}/bashbot.sh" ]; then printf "%s\n" "Bashbot.sh not found in \"${BASHBOT_HOME}\"" exit 4 fi +dev=" Are we in dev or did you forget to run init?" # check for botconfig.jssh readable if [ ! -r "${BASHBOT_ETC}/botconfig.jssh" ]; then - printf "%s\n" "Bashbot config file in \"${BASHBOT_ETC}\" does not exist or is not readable." + printf "%s\n" "Bashbot config file in \"${BASHBOT_ETC}\" does not exist or is not readable. ${dev}" exit 3 fi # check for count.jssh readable if [ ! -r "${BASHBOT_VAR}/count.jssh" ]; then - printf "%s\n" "Bashbot count file in \"${BASHBOT_VAR}\" does not exist or is not readable. Did you run bashbot init?" + printf "%s\n" "Bashbot count file in \"${BASHBOT_VAR}\" does not exist or is not readable. ${dev}" exit 3 fi @@ -60,6 +61,28 @@ FILE_REGEX="${UPLOADDIR}/.*" # get and check ADMIN and NAME BOT_ADMIN="$(getConfigKey "botadmin")" BOT_NAME="$(getConfigKey "botname")" -[[ -z "${BOT_ADMIN}" || "${BOT_ADMIN}" == "?" ]] && printf "%s\n" "${ORANGE}Warning: Botadmin not set, did you forget to sent command${NC} /start" -[[ -z "${BOT_NAME}" ]] && printf "%s\n" "${ORANGE}Warning: Botname not set, did you ever run bashbot?" +ME="${BOT_NAME}" +[[ -z "${BOT_ADMIN}" || "${BOT_ADMIN}" == "?" ]] && printf "%s\n" "${ORANGE}Warning: Botadmin not set, send bot command${NC} /start" +[[ -z "${BOT_NAME}" ]] && printf "%s\n" "${ORANGE}Warning: Botname not set, run bashbot.sh botname" + +# output command result or Telegram response +print_result() { jssh_printDB "BOTSENT" | sort -r; } +print_response() { jssh_printDB "UPD"; } + +# check and output help +print_help() { + case "$1" in + '') + printf "missing arguments\n" + ;& + "-h"*) + printf 'usage: %s\n' "${USAGE}" + exit 1 + ;; + '--h'*) + sed -n '/^#====/,/^#====/p' <"$0" + exit 1 + ;; + esac +} diff --git a/bin/bashbot_init.inc.sh b/bin/bashbot_init.inc.sh new file mode 100644 index 0000000..382b0ec --- /dev/null +++ b/bin/bashbot_init.inc.sh @@ -0,0 +1,143 @@ +#!/bin/bash +#=============================================================================== +# +# FILE: bashbot_init.inc.sh +# +# USAGE: source bashbot_init.inc.sh +# +# DESCRIPTION: extend / overwrite bashbot initialisation +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 27.01.2021 13:42 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== +# shellcheck disable=SC2059 + +########## +# commands to execute before bot_init() is called + + +######## +# called after default init is finished +my_init() { + : # your init here +} + + +######### +# +# extended initialisation: +# +# - uograde old config +# - backup of botconfig.jssh +# - running bot as service or other user +# - copy clean and dist files if not exist +# - configure bot for INLINE CALLBACK MEONLY SILENCER +# +# delete from here to disable extended initialisation +bot_init() { + if [ -n "${BASHBOT_HOME}" ] && ! cd "${BASHBOT_HOME}"; then + printf "Can't change to BASHBOT_HOME" + exit 1 + fi + local runuser chown touser botname DEBUG="$1" + # upgrade from old version + # currently no action + printf "Check for Update actions ...\n" + printf "Done.\n" + # load addons on startup + printf "Initialize modules and addons ...\n" + for addons in "${ADDONDIR:-.}"/*.sh ; do + # shellcheck source=./modules/aliases.sh + [ -r "${addons}" ] && source "${addons}" "init" "${DEBUG}" + done + printf "Done.\n" + # ask for bashbot user + # shellcheck disable=SC2153 + runuser="${RUNUSER}"; [ "${UID}" = "0" ] && runuser="nobody" + printf "Enter User to run bashbot [${runuser}]: " + read -r chown + [ -z "${chown}" ] && chown="${runuser}"; touser="${chown%:*}" + # check user ... + if ! id "${touser}" &>/dev/null; then + printf "${RED}User \"${touser}\" does not exist!${NN}" + exit 3 + elif [[ "${UID}" != "0" && "${touser}" != "${runuser}" ]]; then + # different user but not root ... + printf "${ORANGE}You are not root, adjusting permissions may fail. Try \"sudo ./bashbot.sh init\"${NN}Press to stop or to continue..." 1>&2 + [ -n "${INTERACTIVE}" ] && read -r runuser + fi + # check if mycommands exist + if [ ! -r "${BASHBOT_ETC:-.}/mycommands.sh" ]; then + printf "Mycommands.sh not found, copy ${GREY}lean file, xamples or one${NC} to mycommands.sh? (c/e/N) N\b" + read -r ANSWER + [[ "${ANSWER}" =~ ^[cC] ]] && cp -f "${BASHBOT_ETC:-.}/mycommands.sh.clean" "${BASHBOT_ETC:-.}/mycommands.sh" + [[ "${ANSWER}" =~ ^[eE] ]] && cp -f "${BASHBOT_ETC:-.}/mycommands.sh.dist" "${BASHBOT_ETC:-.}/mycommands.sh" + # offer to copy config also + if [ ! -r "${BASHBOT_ETC:-.}/mycommands.conf" ]; then + printf "Mycommands config file not found, copy ${GREY}mycommands.conf.dist${NC} to mycommands.conf? (Y/n) Y\b" + read -r ANSWER + [[ "${ANSWER}" =~ ^[nN] ]] || cp -f "${BASHBOT_ETC:-.}/mycommands.conf.dist" "${BASHBOT_ETC:-.}/mycommands.conf" + fi + # adjust INLINE CALLBACK MEONLY SILENCER + if [ -w "${BASHBOT_ETC:-.}/mycommands.conf" ]; then + printf "Activate processing for ${GREY}nline queries, allback buttons, oth or one${NC} in mycommands.sh? (i/c/b/N) N\b" + read -r ANSWER + [[ "${ANSWER}" =~ ^[iIbB] ]] && sed -i '/INLINE="/ s/^.*$/export INLINE="1"/' "${BASHBOT_ETC:-.}/mycommands.conf" + [[ "${ANSWER}" =~ ^[cCbB] ]] && sed -i '/CALLBACK="/ s/^.*$/export CALLBACK="1"/' "${BASHBOT_ETC:-.}/mycommands.conf" + printf "Always ignore commands for other Bots in chat ${GREY}(/cmd@other_bot)${NC}? (y/N) N\b" + read -r ANSWER + [[ "${ANSWER}" =~ ^[yY] ]] && sed -i '/MEONLY="/ s/^.*$/export MEONLY="1"/' "${BASHBOT_ETC:-.}/mycommands.conf" + printf "Delete administrative messages in chats ${GREY}(pinned, user join/leave, ...)${NC}? (y/N) N\b" + read -r ANSWER + [[ "${ANSWER}" =~ ^[yY] ]] && sed -i '/SILENCER="/ s/^.*$/export SILENCER="yes"/' "${BASHBOT_ETC:-.}/mycommands.conf" + fi + printf "Done.\n" + fi + # adjust permissions + printf "Adjusting files and permissions for user \"${touser}\" ...\n" + 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 \{\} + + chown -Rf "${chown}" . ./* + printf "Done.\n" + # adjust values in bashbot.rc + if [ -w "bashbot.rc" ]; then + printf "Adjust user and botname in bashbot.rc ...\n" + sed -i '/^[# ]*runas=/ s|runas=.*$|runas="'"${touser}"'"|' "bashbot.rc" + sed -i '/^[# ]*bashbot=/ s|bashbot=.*$|bashbot="cd '"${PWD}"'; '"${PWD}"'/'"${0##*/}"'"|' "bashbot.rc" + botname="$(getConfigKey "botname")" + [ -n "${botname}" ] && sed -i '/^[# ]*name=/ s|name=.*$|name="'"${botname}"'"|' "bashbot.rc" + printf "Done.\n" + fi + # ask to check bottoken online + if [ -z "$(getConfigKey "botid")" ]; then + printf "Seems to be your first init. Should I verify your bot token online? (y/N) N\b" + read -r ANSWER + if [[ "${ANSWER}" =~ ^[Yy] ]]; then + printf "${GREEN}Contacting telegram to verify your bot token ...${NN}" + $0 botname + fi + fi + # check if botconf seems valid + printf "${GREEN}This is your bot config:${NN}${GREY}" + sed 's/^/\t/' "${BOTCONFIG}.jssh" | grep -vF '["bot_config_key"]'; printf "${NC}" + if check_token "$(getConfigKey "bottoken")" && [[ "$(getConfigKey "botadmin")" =~ ^[${o9o9o9}]+$ ]]; then + printf "Bot config seems to be valid. Should I make a backup copy? (Y/n) Y\b" + read -r ANSWER + if [[ -z "${ANSWER}" || "${ANSWER}" =~ ^[^Nn] ]]; then + printf "Copy bot config to ${BOTCONFIG}.jssh.ok ...\n" + cp "${BOTCONFIG}.jssh" "${BOTCONFIG}.jssh.ok" + fi + else + printf "${ORANGE}Bot config may incomplete, pls check.${NN}" + fi + # show result + printf "${GREY}"; ls -ldp "${DATADIR}" "${LOGDIR}" ./*.jssh* ./*.sh ./*.conf 2>/dev/null; printf "${NC}" + _exec_if_function my_init +} diff --git a/bin/bashbot_stats.sh b/bin/bashbot_stats.sh index 53a6b25..df23119 100755 --- a/bin/bashbot_stats.sh +++ b/bin/bashbot_stats.sh @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC1090,SC2034 #=============================================================================== # # FILE: bin/bashbot_stats.sh @@ -16,25 +17,12 @@ USAGE='bashbot_stats.sh [-h|--help] [debug]' # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 23.12.2020 20:34 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== -#### -# parse args -case "$1" in - "-h"*) - printf "usage: %s\n" "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n '3,/###/p' <"$0" - exit 1 - ;; -esac - # set bashbot environment -# shellcheck disable=SC1090 source "${0%/*}/bashbot_env.inc.sh" "$1" +[ -n "$1" ] && print_help "$1" #### # ready, do stuff here ----- diff --git a/bin/delete_message.sh b/bin/delete_message.sh index 6d42a40..8b26769 100755 --- a/bin/delete_message.sh +++ b/bin/delete_message.sh @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC1090,SC2034 #=============================================================================== # # FILE: bin/delete_message.sh @@ -19,32 +20,17 @@ USAGE='delete_message.sh [-h|--help] "CHAT[ID]" "MESSAGE[ID]" [debug]' # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 03.01.2021 15:37 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== #### # parse args DELETE="delete_message" -case "$1" in - '') - printf "missing arguments\n" - ;& - "-h"*) - printf 'usage: %s\n' "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n '3,/###/p' <"$0" - exit 1 - ;; -esac - # set bashbot environment -# shellcheck disable=SC1090 source "${0%/*}/bashbot_env.inc.sh" "${3:-debug}" # $3 debug +print_help "$1" -#### #### # ready, do stuff here ----- if [ "$1" == "BOTADMIN" ]; then @@ -59,5 +45,4 @@ fi [ "${BOTSENT[OK]}" = "true" ] && BOTSENT[ID]="$2" # output send message result -jssh_printDB "BOTSENT" | sort -r - +print_result diff --git a/bin/edit_buttons.sh b/bin/edit_buttons.sh new file mode 100755 index 0000000..0455d1c --- /dev/null +++ b/bin/edit_buttons.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034 +#=============================================================================== +# +# FILE: bin/edit_buttons.sh +# +USAGE='send_message.sh [-h|--help] "CHAT[ID]" "MESSAGE[ID]" "text|url" ...' +# +# DESCRIPTION: send a send buttons in a row to the given user/group +# +# OPTIONS: CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself +# MESSAGE[ID] - ID of MESSAGE with buttons to edit +# text|url - buttons to send, each button as "text|url" pair or +# "url" only to show url as text also, "" starts new row +# "url" not http(s):// or tg:// is sent as callback_data +# +# -h - display short help +# --help - this help +# +# EXAMPLE: 2 buttons on 2 rows, first shows Amazon, second the url as text +# send_buttons.sh "Amazon|https://www.amazon.com" "" "https://mydealz.de" ... +# +# Set BASHBOT_HOME to your installation directory +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 21.01.2021 08:10 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +SEND="edit_inline_keyboard" + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "debug" +print_help "$1" + +#### +# ready, do stuff here ----- +if [ "$1" == "BOTADMIN" ]; then + CHAT="${BOT_ADMIN}" +else + CHAT="$1" +fi +MESSAGE_ID="$2" +shift 2 + +# send message in selected format +"${SEND}" "${CHAT}" "${MESSAGE_ID}" "$(_button_row "$@")" + +# output send message result +print_result diff --git a/bin/edit_message.sh b/bin/edit_message.sh new file mode 100755 index 0000000..dd9b766 --- /dev/null +++ b/bin/edit_message.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034 +#=============================================================================== +# +# FILE: bin/edit_message.sh +# +USAGE='send_edit_message.sh [-h|--help] [format|caption] "CHAT[ID]" "MESSAGE[ID]" "message ...." [debug]' +# +# DESCRIPTION: replace a message in the given user/group +# +# OPTIONS: format - normal, markdown, html or caption for file caption (optional) +# CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself +# MESSAGE[ID] - message to replace +# message - message to send in specified format +# if no format is given send_normal_message() format is used +# +# -h - display short help +# --help - this help +# +# Set BASHBOT_HOME to your installation directory +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 23.12.2020 16:52 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +SEND="edit_normal_message" +case "$1" in + "nor"*|"tex"*) + SEND="edit_normal_message" + shift + ;; + "mark"*) + SEND="edit_markdownv2_message" + shift + ;; + "htm"*) + SEND="edit_html_message" + shift + ;; + "cap"*) + SEND="edit_message_caption" + shift + ;; +esac + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "${4:-debug}" # $4 debug +print_help "$1" + +#### +# ready, do stuff here ----- +if [ "$1" == "BOTADMIN" ]; then + CHAT="${BOT_ADMIN}" +else + CHAT="$1" +fi + +# send message in selected format +"${SEND}" "${CHAT}" "$2" "$3" + +# output send message result +print_result diff --git a/bin/kickban_user.sh b/bin/kickban_user.sh new file mode 100755 index 0000000..cb31df1 --- /dev/null +++ b/bin/kickban_user.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034 +#=============================================================================== +# +# FILE: bin/kickban_user.sh +# +USAGE='kickban_user.sh [-h|--help] [-u|--unban] "CHAT[ID]" "USER[ID]" [debug]' +# +# DESCRIPTION: kickban or unban a user from the given group +# +# OPTIONS: -u | --unban - unban user +# CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself +# USER[ID] - user to (un)ban +# +# -h - display short help +# --help - this help +# +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 25.01.2021 20:34 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +BAN="kick_chat_member" +case "$1" in + "-u"|"--unban") + BAN="unban_chat_member" + shift + ;; +esac + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "${3:-debug}" # $3 debug +print_help "$1" + +#### +# ready, do stuff here ----- + +# send message in selected format +"${BAN}" "$1" "$2" + +# output send message result +print_result diff --git a/bin/process_update.sh b/bin/process_update.sh new file mode 100755 index 0000000..22b9898 --- /dev/null +++ b/bin/process_update.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034,SC2059 +#=============================================================================== +# +# FILE: bin/process_update.sh +# +USAGE='process_update.sh [-h|--help] [debug] [/dev/null)" + +# assign to bashbot ARRAY +Json2Array 'UPD' <<<"${UPDATE}" + +# process telegram update +"${COMMAND}" "0" "$1" + diff --git a/bin/promote_user.sh b/bin/promote_user.sh new file mode 100755 index 0000000..fdcaad0 --- /dev/null +++ b/bin/promote_user.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034 +#=============================================================================== +# +# FILE: bin/promote_user.sh +# +USAGE='promote_user.sh [-h|--help] "CHAT[ID]" "USER[ID]" "right[:true|false]" ..' +# +# DESCRIPTION: promote / denote user rights in given group +# +# OPTIONS: CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself +# USER[ID] - user to (un)ban +# rights[:true|false] - rights to grant in long or short form, +# followed by :true to grant or :false to renove +# long: is_anonymous can_change_info can_post_messages can_edit_messages +# can_delete_messages can_invite_users can_restrict_members +# can_pin_messages can_promote_member` +# short: anon change post edit delete invite restrict pin promote +# +# -h - display short help +# --help - this help +# +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 25.01.2021 22:34 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +PROMOTE="promote_chat_member" + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "debug" # debug +print_help "$1" + +#### +# ready, do stuff here ----- + +# send message in selected format +"${PROMOTE}" "$@" + +# output send message result +print_result diff --git a/bin/send_broadcast.sh b/bin/send_broadcast.sh index 9ccc90c..a3390a3 100755 --- a/bin/send_broadcast.sh +++ b/bin/send_broadcast.sh @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC1090,SC2034 #=============================================================================== # shellcheck disable=SC2059 # @@ -27,7 +28,7 @@ USAGE='broadcast_message.sh [-h|--help] [--doit] [--groups|--both|--db=file] [fo # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 16.12.2020 16:14 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== #### @@ -75,23 +76,11 @@ case "$1" in SEND="send_html_message" shift ;; - '') - printf "missing missing arguments\n" - ;& - "-h"*) - printf 'usage: %s\n' "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n -e '/# shellcheck /d' -e '3,/###/p' <"$0" - exit 1 - ;; esac # set bashbot environment -# shellcheck disable=SC1090 source "${0%/*}/bashbot_env.inc.sh" "$2" # $3 debug - +print_help "$1" # read in users from given DB or count.jssh database="${USERDB:-${COUNTFILE}}" diff --git a/bin/send_buttons.sh b/bin/send_buttons.sh new file mode 100755 index 0000000..f2d2b3e --- /dev/null +++ b/bin/send_buttons.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# shellcheck disable=SC1090,SC2034 +#=============================================================================== +# +# FILE: bin/send_message.sh +# +USAGE='send_message.sh [-h|--help] "CHAT[ID]" "message" "text|url" ...' +# +# DESCRIPTION: send a send buttons in a row to the given user/group +# +# OPTIONS: CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself +# message - message to send +# text|url - buttons to send, each button as "text|url" pair or +# "url" not http(s):// or tg:// is sent as callback_data +# "url" only to show url as text also, "" starts new row +# +# -h - display short help +# --help - this help +# +# EXAMPLE: 2 buttons on 2 rows, first shows Amazon, second the url as text +# send_buttons.sh "Amazon|https://www.amazon.com" "" "https://mydealz.de" ... +# +# Set BASHBOT_HOME to your installation directory +# +# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ +# AUTHOR: KayM (gnadelwartz), kay@rrr.de +# CREATED: 18.01.2021 11:34 +# +#### $$VERSION$$ v1.40-0-gf9dab50 +#=============================================================================== + +#### +# parse args +SEND="send_inline_keyboard" + +# set bashbot environment +source "${0%/*}/bashbot_env.inc.sh" "debug" +print_help "$1" + +#### +# ready, do stuff here ----- +if [ "$1" == "BOTADMIN" ]; then + CHAT="${BOT_ADMIN}" +else + CHAT="$1" +fi +MESSAGE="$2" +shift 2 + +# send message in selected format +"${SEND}" "${CHAT}" "${MESSAGE}" "$(_button_row "$@")" + +# output send message result +print_result diff --git a/bin/send_edit_message.sh b/bin/send_edit_message.sh deleted file mode 100755 index ad8bb38..0000000 --- a/bin/send_edit_message.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -#=============================================================================== -# -# FILE: bin/send_message.sh -# -USAGE='send_edit_message.sh [-h|--help] [format|caption] "CHAT[ID]" "MESSAGE[ID]" "message ...." [debug]' -# -# DESCRIPTION: replace a message in the given user/group -# -# OPTIONS: format - normal, markdown, html or caption for file caption (optional) -# CHAT[ID] - ID number of CHAT or BOTADMIN to send to yourself -# MESSAGE[ID] - message to replace -# message - message to send in specified format -# if no format is given send_normal_message() format is used -# -# -h - display short help -# --help - this help -# -# Set BASHBOT_HOME to your installation directory -# -# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ -# AUTHOR: KayM (gnadelwartz), kay@rrr.de -# CREATED: 23.12.2020 16:52 -# -#### $$VERSION$$ v1.30-0-g3266427 -#=============================================================================== - -#### -# parse args -SEND="edit_normal_message" -case "$1" in - "nor"*|"tex"*) - SEND="edit_normal_message" - shift - ;; - "mark"*) - SEND="edit_markdownv2_message" - shift - ;; - "htm"*) - SEND="edit_html_message" - shift - ;; - "cap"*) - SEND="edit_message_caption" - shift - ;; - '') - printf "missing arguments\n" - ;& - "-h"*) - printf 'usage: %s\n' "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n '3,/###/p' <"$0" - exit 1 - ;; -esac - -# set bashbot environment -# shellcheck disable=SC1090 -source "${0%/*}/bashbot_env.inc.sh" "${4:-debug}" # $4 debug - -#### -#### -# ready, do stuff here ----- -if [ "$1" == "BOTADMIN" ]; then - CHAT="${BOT_ADMIN}" -else - CHAT="$1" -fi - -# send message in selected format -"${SEND}" "${CHAT}" "$2" "$3" - -# output send message result -jssh_printDB "BOTSENT" | sort -r - diff --git a/bin/send_edit_message.sh b/bin/send_edit_message.sh new file mode 120000 index 0000000..e6089f6 --- /dev/null +++ b/bin/send_edit_message.sh @@ -0,0 +1 @@ +edit_message.sh \ No newline at end of file diff --git a/bin/send_file.sh b/bin/send_file.sh index 5a42b2c..1cc2dc2 100755 --- a/bin/send_file.sh +++ b/bin/send_file.sh @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC1090,SC2034 #=============================================================================== # # FILE: bin/send_file.sh @@ -24,29 +25,16 @@ USAGE='send_file.sh [-h|--help] "CHAT[ID]" "file|URL" "caption ...." [type] [deb # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 25.12.2020 20:24 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== #### # parse args SEND="send_file" -case "$1" in - '') - printf "missing arguments\n" - ;& - "-h"*) - printf 'usage: %s\n' "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n '3,/###/p' <"$0" - exit 1 - ;; -esac # set bashbot environment -# shellcheck disable=SC1090 source "${0%/*}/bashbot_env.inc.sh" "${5:-debug}" # $5 debug +print_help "$1" #### # ready, do stuff here ----- @@ -64,5 +52,4 @@ FILE="$2" "${SEND}" "${CHAT}" "${FILE}" "$3" "$4" # output send message result -jssh_printDB "BOTSENT" | sort -r - +print_result diff --git a/bin/send_message.sh b/bin/send_message.sh index 21faf2c..10edd37 100755 --- a/bin/send_message.sh +++ b/bin/send_message.sh @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC1090,SC2034 #=============================================================================== # # FILE: bin/send_message.sh @@ -21,7 +22,7 @@ USAGE='send_message.sh [-h|--help] [format] "CHAT[ID]" "message ...." [debug]' # AUTHOR: KayM (gnadelwartz), kay@rrr.de # CREATED: 16.12.2020 11:34 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== #### @@ -40,22 +41,11 @@ case "$1" in SEND="send_html_message" shift ;; - '') - printf "missing arguments\n" - ;& - "-h"*) - printf 'usage: %s\n' "${USAGE}" - exit 1 - ;; - '--h'*) - sed -n '3,/###/p' <"$0" - exit 1 - ;; esac # set bashbot environment -# shellcheck disable=SC1090 source "${0%/*}/bashbot_env.inc.sh" "${3:-debug}" # $3 debug +print_help "$1" #### # ready, do stuff here ----- @@ -69,5 +59,4 @@ fi "${SEND}" "${CHAT}" "$2" # output send message result -jssh_printDB "BOTSENT" | sort -r - +print_result diff --git a/commands.sh b/commands.sh index f3f95a4..20e5beb 100644 --- a/commands.sh +++ b/commands.sh @@ -8,14 +8,14 @@ # | |__/ / |_| | | | | | |_| | |__ | |____( (_| | | |__ _ # |_____/ \___/ |_| |_|\___/ \___) |_______)____|_|\___)_| # -# this file *MUST* not be edited! place your config and commands in -# the file "mycommands.sh". a clean version is provided as "mycommands.sh.clean" +# this file *MUST* not edited! place your config in the file "mycommands.conf" +# and commands in "mycommands.sh", a clean version is provided as "mycommands.sh.clean" # # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # # bashbot locale defaults to c.UTF-8, adjust locale in mycommands.sh if needed @@ -66,6 +66,7 @@ fi # copy "mycommands.sh.dist" to "mycommands.sh" and change the values there # defaults to no inline, all commands and nonsense home dir export INLINE="0" +export CALLBACK="0" export MEONLY="0" export FILE_REGEX="${BASHBOT_ETC}/.*" @@ -76,14 +77,18 @@ export FILE_REGEX="${BASHBOT_ETC}/.*" if [ -z "$1" ] || [[ "$1" == *"debug"* ]];then - # detect inline commands.... - # no default commands, all processing is done in myinlines() - if [ "${INLINE}" != "0" ] && [ -n "${iQUERY[ID]}" ]; then - # forward iinline query to optional dispatcher - _exec_if_function myinlines + ################# + # detect inline and callback query + if [ -n "${iQUERY[ID]}" ]; then + # forward inline query to optional dispatcher + [ "${INLINE:-0}" != "0" ] && _exec_if_function myinlines - # regular (global) commands ... - # your commands are in mycommands() + elif [ -n "${iBUTTON[ID]}" ]; then + # forward inline query to optional dispatcher + [ "${CALLBACK:-0}" != "0" ] && _exec_if_function mycallbacks + + ################# + # regular command else ################### @@ -127,7 +132,7 @@ if [ -z "$1" ] || [[ "$1" == *"debug"* ]];then '/help'*) send_markdown_message "${CHAT[ID]}" "${bashbot_help}" ;; - '/leavechat'*) # bot leave chat if user is admin in chat + '/leavechat'*) # bot leave chat if user is admin in chat if user_is_admin "${CHAT[ID]}" "${USER[ID]}" || user_is_allowed "${USER[ID]}" "leave" ; then send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*" leave_chat "${CHAT[ID]}" diff --git a/dev/all-tests.sh b/dev/all-tests.sh index d0a2051..9fb4847 100755 --- a/dev/all-tests.sh +++ b/dev/all-tests.sh @@ -5,7 +5,7 @@ # # Description: run all tests, exit after failed test # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############################################################# #shellcheck disable=SC1090 diff --git a/dev/dev.inc.sh b/dev/dev.inc.sh index fa0fd75..b47d121 100644 --- a/dev/dev.inc.sh +++ b/dev/dev.inc.sh @@ -5,7 +5,7 @@ # # Description: common stuff for all dev scripts # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############################################################# # magic to ensure that we're always inside the root of our application, @@ -20,3 +20,5 @@ else printf "Sorry, no git repository %s\n" "$(pwd)" && exit 1 fi +HOOKDIR="dev/hooks" +LASTCOMMIT=".git/.lastcommit" diff --git a/dev/git-add.sh b/dev/git-add.sh index 39c94eb..ef6bf7d 100755 --- a/dev/git-add.sh +++ b/dev/git-add.sh @@ -3,15 +3,21 @@ # # works together with git pre-push.sh and ADD all changed files since last push -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #shellcheck disable=SC1090 source "${0%/*}/dev.inc.sh" -[ ! -f .git/.lastcommit ] && printf "No previous commit or hooks not installed, use \"git add\" instead ... Abort\n" && exit +# check for last commit date +if [ ! -f "${LASTCOMMIT}" ]; then + if ! touch -d "$(git log -1 --format=%cD)" "${LASTCOMMIT}"; then + printf "No previous commit found, use \"git add\" instead ... Abort\n" + exit + fi +fi set +f -FILES="$(find ./* -newer .git/.lastcommit| grep -v -e 'DIST\/' -e 'STANDALONE\/' -e 'JSON.sh')" +FILES="$(find ./* -newer "${LASTCOMMIT}" | grep -v -e 'DIST\/' -e 'STANDALONE\/' -e 'JSON.sh')" set -f # FILES="$(find ./* -newer .git/.lastpush)" [ "${FILES}" = "" ] && printf "Nothing changed since last commit ...\n" && exit diff --git a/dev/hooks/post-commit.sh b/dev/hooks/post-commit.sh index e988e64..1443899 100755 --- a/dev/hooks/post-commit.sh +++ b/dev/hooks/post-commit.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############ # NOTE: you MUST run install-hooks.sh again when updating this file! diff --git a/dev/hooks/pre-commit.sh b/dev/hooks/pre-commit.sh index 8b44725..66cd4cb 100755 --- a/dev/hooks/pre-commit.sh +++ b/dev/hooks/pre-commit.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############ # NOTE: you MUST run install-hooks.sh again when updating this file! diff --git a/dev/hooks/pre-push.sh b/dev/hooks/pre-push.sh index 710c411..6adae54 100755 --- a/dev/hooks/pre-push.sh +++ b/dev/hooks/pre-push.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############ # NOTE: you MUST run install-hooks.sh again when updating this file! diff --git a/dev/inject-json.sh b/dev/inject-json.sh index 0936571..06f2013 100644 --- a/dev/inject-json.sh +++ b/dev/inject-json.sh @@ -7,7 +7,7 @@ # # Usage: source inject-json.sh # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############################################################## # download JSON.sh diff --git a/dev/install-hooks.sh b/dev/install-hooks.sh index e4e41b4..4137629 100755 --- a/dev/install-hooks.sh +++ b/dev/install-hooks.sh @@ -1,13 +1,11 @@ #!/usr/bin/env bash # this has to run once atfer git clone # and every time we create new hooks -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #shellcheck disable=SC1090 source "${0%/*}/dev.inc.sh" -HOOKDIR="dev/hooks" - printf "Installing hooks..." for hook in pre-commit post-commit pre-push do diff --git a/dev/make-distribution.sh b/dev/make-distribution.sh index 226cd28..cd5bb7d 100755 --- a/dev/make-distribution.sh +++ b/dev/make-distribution.sh @@ -7,7 +7,7 @@ # # Options: --notest - skip tests # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############################################################## #shellcheck disable=SC1090 diff --git a/dev/make-distribution.sh.exclude b/dev/make-distribution.sh.exclude index 2a21666..c03a2d7 100644 --- a/dev/make-distribution.sh.exclude +++ b/dev/make-distribution.sh.exclude @@ -1,7 +1,9 @@ data-bot-bash/* +webhook-fifo JSON.awk bashbot.rc mycommands.sh +mycommands.conf awk-patch.sh *.jssh* botacl diff --git a/dev/make-html.sh b/dev/make-html.sh index f3cc5f8..46debef 100644 --- a/dev/make-html.sh +++ b/dev/make-html.sh @@ -7,7 +7,7 @@ # # Usage: source make-hmtl # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ############################################################## # check for correct dir diff --git a/dev/make-standalone.sh b/dev/make-standalone.sh index a9f9e69..337ede9 100755 --- a/dev/make-standalone.sh +++ b/dev/make-standalone.sh @@ -11,7 +11,7 @@ # If you your bot is finished you can use make-standalone.sh to create the # the old all-in-one bashbot: bashbot.sh and commands.sh only! # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ################################################################### #shellcheck disable=SC1090 @@ -73,13 +73,14 @@ printf "\n... create unified bashbot.sh\n" # first head of bashbot.sh sed -n '0,/for module in/ p' bashbot.sh | head -n -3 - # then mycommands from first non comment line on + # then modules without shebang printf '\n##############################\n# bashbot modules starts here ...\n' - cat modules/*.sh | sed -e 's/^#\!\/bin\/bash.*//' + # shellcheck disable=SC2016 + cat modules/*.sh | sed -e 's/^#\!\/bin\/bash.*//' -e '/^#.*\$\$VERSION\$\$/d' - # last tail of commands.sh - printf '\n##############################\n# bashbot internal functions starts here ...\n\n' - sed -n '/BASHBOT INTERNAL functions/,$ p' bashbot.sh + # last remaining commands.sh + printf '\n##############################\n' + sed -n '/^# read commands file/,$ p' bashbot.sh } >>$$bashbot.sh @@ -90,11 +91,11 @@ rm -rf modules printf "Create minimized Version of bashbot.sh and commands.sh\n" # shellcheck disable=SC2016 -sed -E -e '/(shellcheck)|(^#!\/)|(\$\$VERSION\$\$)/! s/^[[:space:]]*#.*//' -e 's/^[[:space:]]*//' -e '/^$/d' -e 'N;s/\\\n/ /;P;D' bashbot.sh |\ - sed 'N;s/\\\n/ /;P;D' > bashbot.sh.min +sed -E -e '/(shellcheck)|(^#!\/)|(\$\$VERSION\$\$)/! s/^[[:space:]]*#.*//' -e '/shellcheck/! s/\t+#.*//' -e 's/^[[:space:]]*//'\ + -e '/^$/d' bashbot.sh | sed 'N;s/\\\n/ /;P;D' | sed 'N;s/\\\n/ /;P;D' > bashbot.sh.min # shellcheck disable=SC2016 -sed -E -e '/(shellcheck)|(^#!\/)|(\$\$VERSION\$\$)/! s/^[[:space:]]*#.*//' -e 's/^[[:space:]]*//' -e 's/\)[[:space:]]+#.*/)/' -e '/^$/d' commands.sh |\ - sed 'N;s/\\\n/ /;P;D' > commands.sh.min +sed -E -e '/(shellcheck)|(^#!\/)|(\$\$VERSION\$\$)/! s/^[[:space:]]*#.*//' -e '/shellcheck/! s/\t+#.*//' -e 's/^[[:space:]]*//'\ + -e '/^$/d' commands.sh | sed 'N;s/\\\n/ /;P;D' > commands.sh.min chmod +x bashbot.sh.min # make html doc diff --git a/dev/obfuscate.sh b/dev/obfuscate.sh index b921e13..d2c44bb 100755 --- a/dev/obfuscate.sh +++ b/dev/obfuscate.sh @@ -2,7 +2,7 @@ # # joke hack to obfuscate bashbot.min.sh # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # shellcheck disable=SC2028,SC2016,SC1117 infile="bashbot.sh" diff --git a/dev/shellcheck.files b/dev/shellcheck.files index f43ba31..025dba0 100644 --- a/dev/shellcheck.files +++ b/dev/shellcheck.files @@ -1,4 +1,5 @@ # list of additional files to check from shellcheck -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 bashbot.rc +mycommands.conf mycommands.sh.clean diff --git a/dev/version.sh b/dev/version.sh index 5090119..ed733bf 100755 --- a/dev/version.sh +++ b/dev/version.sh @@ -1,6 +1,6 @@ #!/bin/bash # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # shellcheck disable=SC2016 # # Easy Versioning in git: @@ -43,7 +43,8 @@ unset IFS VERSION="$(git describe --tags --long)" printf "Update to version %s ...\n" "${VERSION}" -FILES="$(find ./*)" +# only regular files, ignore .dot files/dirs, e.g. .git .gitinore in BASEDIR +FILES="$(find ./* -type f)" [ "$1" != "" ] && FILES="$*" # autogenerate REMADME.html REMADE.txt @@ -53,7 +54,7 @@ if [[ "${FILES}" == *"README.md"* ]]; then cat "doc/bashbot.ascii" >"README.txt" if [ -r "README.html" ] && type -f html2text >/dev/null; then # convert html links to text [link] - sed -E 's/([^<#]+)<\/a>/\2 [\1]/' ([^<#]+)<\/a>/\2 [\1]/g' >README.txt else type -f fold >/dev/null && fold -s -w 90 README.md >>README.txt @@ -63,7 +64,8 @@ fi # change version string in given files for file in ${FILES} do - [ ! -f "${file}" ] && continue + # symlink is a file :-( + [[ -L "${file}" || ! -f "${file}" ]] && continue #[ "${file}" == "version" ] && continue printf "%s" " ${file}" >&2 sed -i 's/^#### $$VERSION$$.*/#### \$\$VERSION\$\$ '"${VERSION}"'/' "${file}" diff --git a/doc/0_install.md b/doc/0_install.md index 199e604..63674b8 100644 --- a/doc/0_install.md +++ b/doc/0_install.md @@ -132,5 +132,5 @@ You must update to [Version 1.20](https://github.com/topkecleon/telegram-bot-bas #### [Next Create Bot](1_firstbot.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/1_firstbot.md b/doc/1_firstbot.md index 173c936..0d176c9 100644 --- a/doc/1_firstbot.md +++ b/doc/1_firstbot.md @@ -65,5 +65,5 @@ group. This step is up to you actually. #### [Prev Installation](0_install.md) #### [Next Getting started](2_usage.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/2_usage.md b/doc/2_usage.md index 6ec08bf..4910931 100644 --- a/doc/2_usage.md +++ b/doc/2_usage.md @@ -33,14 +33,19 @@ Have FUN! ├── JSON.sh # bashbots JSON parser, see https://github.com/dominictarr/JSON.sh │ ├── bin # ready to use scripts, use `scriptname --help` for help +│   ├── bashbot_stats.sh # does what it says ... +│   ├── send_broadcast.sh # send message to all known chats │   ├── send_message.sh # send message to given chat │   ├── edit_message.sh # replace given message id in given chat -│   ├── delete_message.sh # delete given message id in given chat -│   ├── send_broadcast.sh # send message to all known chats │   ├── send_file.sh # send file to given chat -│   ├── bashbot_stats.sh # does what it says ... +│   ├── delete_message.sh # delete given message id in given chat +│   ├── send_buttons.sh # send message with attached button +│   ├── edit_buttons.sh # attach/edit message buttons +│   ├── kickban_user.sh # kick/unban user from given chat +│   ├── promote_user.sh # promote/dente user rights in given chat │ │ -│   └── bashbot_env.inc.sh # bashbot location included from scripts, adapt if needed +│   └── bashbot_env.inc.sh # sourced from scripts, adapt locations if needed +│   └── bashbot_init.inc.sh # sourced from bashbot.sh init │ ├── scripts # place your bashbot interactive and background scripts here │   └── interactive.sh.clean # interactive script template for new scripts @@ -91,6 +96,7 @@ Start or Stop your Bot use the following commands: ``` ### Scripts in bin/ +Use `script.sh -h` or `script --help` to get short/long help for script. To count the total number of users and messages run the following command: @@ -244,8 +250,7 @@ e.g. if a new user joins a chat MESSAGE is set to "/_new_chat_user". ### Inline query messages - -Inline query messages are small, non regular messages used for interaction with the user, +Inline query messages are special messages used for interaction with the user, they contain the following variables only: * `${iQUERY}`: Current inline query @@ -256,6 +261,22 @@ they contain the following variables only: * `${iQUERY[LAST_NAME]}`: User's last name +### Callback button messages +Callback button messages special messages swedn from callback buttons, +they contain the following variables only: + +* `$iBUTTON`: This array contains the ID, First name, last name, username and user id of the user clicked on the button + * `${iBUTTON[ID]}`: Callback query ID + * `${iBUTTON[DATA]`: Data attached to button, hopefully unique + * `${iBUTTON[CHAT_ID]`: Chat where button was pressed + * `${iBUTTON[MESSAGE_ID]`: Message to which button is attached + * `${iBUTTON[MESSAGE]`: Text of message + * `${iBUTTON[USER_ID]}`: User's id + * `${iBUTTON[FIRST_NAME]}`: User's first name + * `${iBUTTON[LAST_NAME]}`: User's last name + * `${iBUTTON[USERNAME]}`: User's @username + + ## Send data / get response After every `send_xxx` `get_xxx` call the array BOTSENT contains the most important values from Telegram response. @@ -274,7 +295,7 @@ In case you need other response values , the array `UPD` contains complete Teleg ## Usage of bashbot functions #### sending messages -To send messages use the `send_xxx_message`functions. +To send messages use the `send_xxx_message` functions. To insert line brakes in a message place `\n` in the text. To send regular text without any markdown use: @@ -290,7 +311,7 @@ To send text with html: send_html_message "${CHAT[ID]}" "lol bold" ``` -To forward messages use the `forward`function: +To forward messages use the `forward` function: ```bash forward "${CHAT[ID]}" "from_chat_id" "message_id" ``` @@ -328,20 +349,20 @@ To send local files or URL's (photo, video, voice, sticker, documents) use the ` send_file "${CHAT[ID]}" "/home/user/dog.jpg" "Lool" "photo" send_file "${CHAT[ID]}" "https://images-na.ssl-images-amazon.com/images/I/81DQ0FpoSNL._AC_SL1500_.jpg" ``` -To send custom keyboards use the `send_keyboard`function: +To send custom keyboards use the `send_keyboard` function: ```bash send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" '[ "Yep" , "No" ]' # note the single quotes! send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" "[ \\"Yep\\" , \\"No\\" ]" # within double quotes you must escape the inside double quots ``` -To send locations use the `send_location`function: +To send locations use the `send_location` function: ```bash send_location "${CHAT[ID]}" "Latitude" "Longitude" ``` -To send venues use the `send_venue`function: +To send venues use the `send_venue` function: ```bash send_venue "${CHAT[ID]}" "Latitude" "Longitude" "Title" "Address" "optional foursquare id" ``` -To send a chat action use the `send_action`function. +To send a chat action use the `send_action` function. Allowed values: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for locations. ```bash send_action "${CHAT[ID]}" "action" @@ -351,5 +372,5 @@ send_action "${CHAT[ID]}" "action" #### [Prev Create Bot](1_firstbot.md) #### [Next Advanced Usage](3_advanced.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/3_advanced.md b/doc/3_advanced.md index 8ba9cf4..7f781b1 100644 --- a/doc/3_advanced.md +++ b/doc/3_advanced.md @@ -144,7 +144,7 @@ echo "Text that will appear in one message \nwith this text on a new line" ``` In case you want extend a message already containing a location, a file, a keyboard etc., -with an additionial text simply add ` mytextstartshere additional text`at the end of the string: +with an additionial text simply add ` mytextstartshere additional text` at the end of the string: ```bash out="Text that will appear mylatstartshere 45 mylongstartshere 45" [[ "$out" != *'in chat'* ]] && out="$out mytextstartshere in chat." @@ -190,7 +190,7 @@ Note: Background jobs run independent from main bot and continue running until y In order to enable **inline mode**, send `/setinline` command to [@BotFather](https://telegram.me/botfather) and provide the placeholder text that the user will see in the input field after typing your bot’s name. -The following commands allows you to send ansers to *inline queries*. To enable bashbot to process inline queries set `INLINE="1"`in 'mycommands.sh'. +The following commands allows you to send ansers to *inline queries*. To enable bashbot to process inline queries set `INLINE="1"` in `mycommands.sh`. To send messages or links through an *inline query*: ```bash @@ -258,7 +258,7 @@ By default you don't have to care about retry, as bashbot resend the message aft Only if the retry fails also an error is returned. The downside is that send_message functions will wait until resend is done. If you want to disable automatic error processing and handle all errors manually (or don't care) -set `BASHBOT_RETRY`to any no zero value. +set `BASHBOT_RETRY` to any no zero value. [Telegram Bot API error codes](https://github.com/TelegramBotAPI/errors) @@ -266,9 +266,9 @@ set `BASHBOT_RETRY`to any no zero value. #### Detect bot blocked If the we can't connect to telegram, e.g. blocked from telegram server but also any other reason, -bashbot set `BOTSENT[ERROR]`to `999`. +bashbot set `BOTSENT[ERROR]` to `999`. -To get a notification on every connection problem create a function named `bashbotBlockRecover`and handle blocks there. +To get a notification on every connection problem create a function named `bashbotBlockRecover` and handle blocks there. If the function returns true (0 or no value) bashbot will retry once and then return to the calling function. In case you return any non 0 value bashbot will return to the calling function without retry. @@ -302,5 +302,5 @@ Note: If you disable automatic retry, se above, you disable also connection prob #### [Prev Getting started](2_usage.md) #### [Next Expert Use](4_expert.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/4_expert.md b/doc/4_expert.md index f15d390..774cde0 100644 --- a/doc/4_expert.md +++ b/doc/4_expert.md @@ -434,5 +434,5 @@ for every poll until the maximum of BASHBOT_SLEEP ms. #### [Prev Advanced Use](3_advanced.md) #### [Next Best Practice](5_practice.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/5_practice.md b/doc/5_practice.md index 85e9f1c..4c9cbe0 100644 --- a/doc/5_practice.md +++ b/doc/5_practice.md @@ -12,7 +12,7 @@ If you don't have a github account, it may time to [setup a free account now](ht ### Add commands to mycommands.sh only Do not change `bashbot.sh` and `commands.sh`, instead place your commands in to `mycommands.sh`. To start with a clean/minimal bot copy `mycommands.sh.clean` to `mycommands.sh` and start editing -the message strings and place commands in the`case ... esac` block of the function mycommands(): +the message strings and place commands in the` case ... esac` block of the function mycommands(): ```bash # file: mycommands.sh # your additional bashbot commands @@ -160,5 +160,5 @@ The second warning is about an unused variable, this is true because in our exam #### [Prev Best Practice](5_practice.md) #### [Next Functions Reference](6_reference.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/6_reference.md b/doc/6_reference.md index 463e13d..30be34a 100644 --- a/doc/6_reference.md +++ b/doc/6_reference.md @@ -8,7 +8,7 @@ To insert line brakes in a message or caption you can place `\n` in the text. ##### send_action `send_action` shows users what your bot is currently doing. -*usage:* send_action "${CHAT[ID]}" "action" +*usage:* send_action "CHAT[ID]" "action" *"action":* `typing`, `upload_photo`, `record_video`, `upload_video`, `record_audio`, `upload_audio`, `upload_document`, `find_location`. @@ -23,7 +23,7 @@ send_action "${CHAT[ID]}" "record_audio" ##### send_normal_message `send_normal_message` sends text only messages to the given chat. -*usage:* send_normal_message "${CHAT[ID]}" "message" +*usage:* send_normal_message "CHAT[ID]" "message" *alias:* _normal_message "message" @@ -41,7 +41,7 @@ has more formatting codes and is more robust, but incompatible with old telegram To send characters reserved for markdown v2 formatting, you must prefix them with `\` ( e.g. `\| \= \_ \*`).\ *Hint*: If a message is not sent, have a look in `logs/ERROR.log` -*usage:* send_markdownv2_message "${CHAT[ID]}" "markdown message" +*usage:* send_markdownv2_message "CHAT[ID]" "markdown message" *example:* ```bash @@ -55,7 +55,7 @@ send_markdownv2_message "${CHAT[ID]}" "*bold* __underlined__ [text](link)" This is the old, legacy Telegram markdown style, retained for backward compatibility. It supports a [reduced set of Markdown](https://core.telegram.org/bots/api#markdown-style) only -*usage:* send_markdown_message "${CHAT[ID]}" "markdown message" +*usage:* send_markdown_message "CHAT[ID]" "markdown message" *alias:* _markdown "message" @@ -70,7 +70,7 @@ send_markdown_message "${CHAT[ID]}" "*bold* _italic_ [text](link)" `send_html_message` sends HTML style messages to the given chat. Telegram supports a [reduced set of HTML](https://core.telegram.org/bots/api#html-style) only -*usage:* send_html_message "${CHAT[ID]}" "html message" +*usage:* send_html_message "CHAT[ID]" "html message" *alias:* _html_message "message" @@ -96,7 +96,7 @@ See also [Text formatting options](https://core.telegram.org/bots/api#formatting ##### delete_message A bot can only delete messages if he is admin of a Chat, if not he can delete his own messages only. -*usage:* delete_message "${CHAT[ID]}" "${MESSAGE[ID]}" +*usage:* delete_message "CHAT[ID]" "${MESSAGE[ID]}" See also [deleteMessage limitations](https://core.telegram.org/bots/api#deletemessage) @@ -107,7 +107,7 @@ See also [deleteMessage limitations](https://core.telegram.org/bots/api#deleteme The main use case for send_message is to process the output of interactive chats and background jobs. **For regular Bot commands I recommend using of the dedicated send_xxx_message() functions from above.** -*usage:* send_message "${CHAT[ID]}" "message" +*usage:* send_message "CHAT[ID]" "message" *example:* - see [Usage](2_usage.md#send_message) and [Advanced Usage](3_advanced.md#Interactive-Chats) @@ -118,7 +118,7 @@ The main use case for send_message is to process the output of interactive chats ##### send_file send_file can send local files, URL's or file_id's as different filex types (_e.g. photo video sticker_) -*usage:* send_file "${CHAT[ID]}" "file/URL/file_id" "caption" ["type"] +*usage:* send_file "CHAT[ID]" "file/URL/file_id" "caption" ["type"] URL's must start with `http://` or `https://` and remote server must send an appropriate media type. A file_id must start with `file_id://`, all other file names are threated as local files. @@ -158,7 +158,7 @@ send_file "${CHAT[ID]}" "dog.jpg" "My Dog" ##### send_album -*usage:* send_album "${CHAT[ID]}" "URL1" "URL2" ... "URLn" +*usage:* send_album "CHAT[ID]" "URL1" "URL2" ... "URLn" *example:* ```bash @@ -166,25 +166,27 @@ send_album "$(getConfigKey "botadmin")" "http://www.rrr.de/slider/main-image1.jp ``` ##### send_location -*usage:* send_location "${CHAT[ID]}" "Latitude" "Longitude" +*usage:* send_location "CHAT[ID]" "Latitude" "Longitude" ##### send_venue -*usage:* send_venue "${CHAT[ID]}" "Latitude" "Longitude" "Title" "Address" "foursquare id (optional)" +*usage:* send_venue "CHAT[ID]" "Latitude" "Longitude" "Title" "Address" "foursquare id (optional)" + + +##### send_sticker +`send_sticker` sends a sticker using a `file_id` to send a sticker that exists on the Telegram servers. + +*usage:* send_sticker "$CHAT[ID]" "file_id" ---- ##### send_keyboard -Note: Since version 0.6 send_keyboard was changed to use native "JSON Array" notation as used from Telegram. -Detection and emulation for old format will be removed after 1.0 release! +`send_keyboard` sends a custom keyboard, Telegram clients will show it instead of the regular keyboard. +If the user press a button on the custom keyboard, the text shown on the button is send to the chat. Example Keyboard Array definitions: -- yes no in two rows: - - OLD format: 'yes' 'no' (two strings) - - NEW format: '[ "yes" ] , [ "no" ]' (two arrays with a string) -- new layouts made easy with NEW format: - Yes No in one row: '[ "yes" , "no" ]' - Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]' - number pad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]' @@ -205,56 +207,263 @@ _keyboard_numpad ``` ##### remove_keyboard +`remove_keyboard` deletes the last custom keyboard. Depending on used Telegram client this will hide or delete the custom keyboard. + *usage:* remove_keybord "$CHAT[ID]" "message" *alias:* _del_keyboard "message" *See also: [Keyboard Markup](https://core.telegram.org/bots/api/#replykeyboardmarkup)* + ---- ##### send_button +`send_button` sends a text message with a single button to open an URL attached. + *usage:* send_button "$CHAT[ID]" "message" "text" "URL" *alias:* _button "text" "URL" *example:* ```bash -send_button "${CHAT[ID]}" "MAKE MONEY FAST!!!" "Visit my Shop" "https://dealz.rrr.de" +send_button "${CHAT[ID]}" "Awesome Deals!" "Visit my Shop" "https://dealz.rrr.de" ``` -##### send_sticker -`send_sticker` sends a sticker using a `file_id` to send a sticker that exists on the Telegram servers. +### Inline buttons +Functions to send/edit messages with with some buttons attached. -*usage:* send_sticker "$CHAT[ID]" "file_id" +##### send_inline_buttons +`senbd_inline_buttons` sends a message with multiple buttons attached. Buttons can be an URL or a CALLBACK button. +By default all buttons are displayed on one row, an empty string `""` starts a new row. +*usage:* send_inline_buttons "CHAT[ID]" "text|url" "text|url" "" "url" "" "text|url" ... -##### send_inline_keyboard -Even its called keyboard, this function is different from send_keyboard. The main difference is that it's only possible to -specify URL buttons, no Text Buttons and the Buttons must be an Array of Buttons as specified for -[Telegram InlineMarkup](https://core.telegram.org/bots/api#inlinekeyboardmarkup). +URL buttons are specified as a `"text|url"` pair separated by `|`, `text` is shown on the button and `url` is opened on button click. +If `"url"` without text is given, `url` is shown on the button and opened on button click. -The inline buttons must be specified as a JSON string in the following format: +*Important* An `url` not startung with http(s):// or tg:// will create a +[CALLBACK Button](https://core.telegram.org/bots/2-0-intro#callback-buttons). -`[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ]``` - -Each button consists of a pair of text and URL values, sourrounded by '{ }', multiple buttons are separated by '**,**' and everything is wrapped in '[ ]'. - -*usage:* send_inline_keyboard "chat-id" "message" "[ {"text":"text", "url":"url"} ...]" - -*alias:* _inline_keyboard "[{"text":"text", "url":"url"} ...]" *example:* ```bash -send_inline_keyboard "${CHAT[ID]}" "MAKE MONEY FAST!!!" '[{"text":"Visit my Shop", url"":"https://dealz.rrr.de"}]' -send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"button 1", url"":"url 1"}, {"text":"button 2", url"":"url 2"} ]' -send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"b 1", url"":"u 1"}, {"text":"b 2", url"":"u 2"}, {"text":"b 2", url"":"u 2"} ]' +# one button, same as send_button +send_inline_buttons "${CHAT[ID]}" "Best Dealz!" "Visit my Shop|https://dealz.rrr.de" + +# result + Best Dealz! + +----------------------------+ + | Visit my Shop | + +----------------------------+ + +# one button row +send_inline_buttons "${CHAT[ID]}" "message" "Button 1|http://rrr.de" "Button 2|http://rrr.de" + +# result + message ... + +----------------------------+ + | Button 1 | Button 2 | + +----------------------------+ + +# multiple button rows +send_inline_buttons "${CHAT[ID]}" "message" "Button 1|http://rrr.de" "Button 2|http://rrr.de" "" "Button on second row|http://rrr.de" + +# result + message ... + +----------------------------+ + | Button 1 | Button 2 | + |----------------------------| + | Button on second row | + +----------------------------+ + +``` + +##### edit_inline_buttons +`edit_inline_buttons` add inline buttons to existing messages, existing inline buttons will be replaced. +Only the attached buttons will be changed, not the message. + +*usage:* edit_inline_buttons "CHAT[ID]" "MESSAGE[ID]" "text|url" "text|url" ... + + +*example:* +```bash +# message without button +send_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message ..." +echo ${BOTSEND[ID]} +567 + +# add one button row +edit_inline_keyboard "${CHAT[ID]}" "567" "button 1|http://rrr.de" "button 2|http://rrr.de" + +# change buttons +edit_inline_keyboard "${CHAT[ID]}" "567" "Success edit_inline_keyboard|http://rrr.de" + +# delete button by replace whole message +edit_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message inline *removed*..." + +``` + +##### answer_callback_query +Each request send from a CALLBACK button must be answered by a call to `answer_callback_query`. +If alert is given an alert will be shown by the Telegram client instead of a notification. + +*usage:* answer_callback_query "iBUTTON[ID]" "text notification ..." ["alert"] + +*example:* +```bash +answer_callback_query "${iBUTTON[ID]}" "Button data is: ${iBUTTON[DATA]}" + +answer_callback_query "${iBUTTON[ID]}" "Alert: Button pressed!" "alert" +``` + + +```bash +# CALLBACK button example +send_inline_buttons "${CHAT[ID]}" "Press Button ..." " Button |RANDOM-BUTTON" + +# result + Press Button ... + +----------------------------+ + | Button | + +----------------------------+ + +# react on button press from mycommands + CALLBACK="1" # enable callbacks +... + mycallbacks() { + local answer + ####################### + # callbacks from buttons attached to messages will be processed here + if [ "${iBUTTON[DATA]}" = "RANDOM-BUTTON" ]; then + answer="Button pressed" + edit_inline_buttons "${iBUTTON[CHAT_ID]}" "${iBUTTON[MESSAGE_ID]}" " Button ${RANDOM}|RANDOM-BUTTON" + fi + + # Telegram needs an ack each callback query, default empty + answer_callback_query "${iBUTTON[ID]}" "${answer}" + ;; + } + +# result, XXXXX: random number changed on each press + Press Button ... + +----------------------------+ + | Button XXXXXX | + +----------------------------+ + +``` + +---- + +#### Inline keyboards +Functions to send/edit more complex button layouts (keyboards), I suggest to start with the simpler inline buttons above. + +##### _button_row +`_button_row` is a helper function to specify a keyboard row in the form "text|url" pairs. +Internally used by inline buttons also. + +*usage:* _button_row "text|url" "text|url" "url" "text|url" ... + +*example:* +```bash +# similar to send_button +send_inline_keyboard "${CHAT[ID]}" "Best Dealz!" "$(_button_row "Visit my Shop|https://dealz.rrr.de")" + +# similar to send_inline_button +send_inline_keyboard "${CHAT[ID]}" "message" "$(_button_row "button 1|http://rrr.de" "button 2|http://rrr.de")" + +# multiple button rows +send_inline_keyboard "${CHAT[ID]}" "message" "$(_button_row "b1|http://rrr.de" "b2|http://rrr.de" "" "b3|http://rrr.de" "b4|http://rrr.de")" +``` + +##### send_inline_keyboard +`send_inline_keyboard` sends a message with keyboards attached, keyboards must be specified in JSON format. + +*usage:* send_inline_keyboard "CHAT[ID]" "message" "[JSON button array]" + +I suggest to use `_button_row` to create the used JSON. For hand crafted JSON the following format must be used, +see [Inline Keyboard Markup](https://core.telegram.org/bots/api#inlinekeyboardmarkup) + +URL `[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ],[...]`\ +CALLBACK `[ {"text":"text1", "callback_data":"abc"}, ... {"text":"textN", "callback_data":"defg"} ],[...]`\ +An URL Button opens the given URL, a CALLBACK button sends an update the bot must react on. + +*example:* +```bash +# send_button +send_inline_keyboard "${CHAT[ID]}" "Best Dealz!" '[{"text":"Visit my Shop", "url":"https://dealz.rrr.de"}]' + +# send_inline_button +send_inline_keyboard "${CHAT[ID]}" "message" '[{"text":"button 1", url"":"http://rrr.de"}, {"text":"button 2", "url":"http://rrr.de"} ]' + +# multiple button rows +send_inline_keyboard "${CHAT[ID]}" "message" '[{"text":"b1", "url":"http://rrr.de"}, {"text":"b2", "url":"http://rrr.de"}], [{"text":"b3", "url":"http://rrr.de"}, "text":"b4", "url":"http://rrr.de"}]' + +# more complex keyboard, note the , +keyboard_text="Deal-O-Mat public groups ..." +keyboard_json="$(_button_row "🤖 #Home of Deal-O-Mat Bot 🤖|https://dealz.rrr.de/dealzbot.html") +, $(_button_row "Amazon DE|https://t.me/joinchat/IvvRtlxxxxx" "Home & Family|https://t.me/joinchat/VPh_wexxxxx") +, $(_button_row "Amz International |https://t.me/joinchat/IvvRtkxxxxx" "Amazon WHD|https://t.me/joinchat/IvvRxxxxx") +, $(_button_row "Smartphones|https://t.me/joinchat/IvvRthtqxxxxx" "Gaming|https://t.me/joinchat/IvvRthRyrsmxxxxx") +, $(_button_row "Accessoires|https://t.me/joinchat/IvvRthlJxxxxx" "eBay|https://t.me/joinchat/IvvRthxxxxx") +, $(_button_row "!! Offtopic Discussions !!|https://t.me/joinchat/IvvRthRhxxxxx-pZrWw") +, $(_button_row "Deals >100|https://t.me/joinchat/IvvRtxxxxx" "Leasing|https://t.me/joinchat/IvvRthRbxxxxx") +, $(_button_row "Deals >1000|https://t.me/joinchat/IvvRtlxxxxx" "Deals >500|https://t.me/joinchat/IvvRthvbHxxxxx") + +send_inline_keyboard "CHAT[ID]" "${keyboard_text}" "${keyboard_json}" + +# result + +---------------------------------+ + | 🤖 #Home of Deal-O-Mat Bot 🤖 | + |---------------------------------| + | Amazon DE | Home & Family | + |----------------|----------------| + | Amz Internat | Amazon WHD | + |----------------|----------------| + | Smartphones | Gaming | + |----------------|----------------| + | Accessoires | eBay | + |---------------------------------| + | !! Offtopic Discussions !! | + |---------------------------------| + | Deals >100 | Leasing | + |----------------|----------------| + | Deals >1000 | Deals >500 | + +---------------------------------+ + ``` *See also [Inline keyboard markup](https://core.telegram.org/bots/api/#inlinekeyboardmarkup)* +##### edit_inline_keyboard +`edit_inline_keyboard` add inline keyboards to existing messages and replace existing inline keyboards. +Only the attached keyboard will be changed, not the message. + +*usage:* edit_inline_keyboard "CHAT[ID]" "MESSAGE[ID]" "[JSON button array]" + +To create a JSON button array I suggest to use `_button_row`. + +*example:* +```bash +# message without button +send_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message ..." +echo ${BOTSEND[ID]} +567 + +# add one button row with help of _button_row +edit_inline_keyboard "${CHAT[ID]}" "567" "$(_button_row "button 1|http://rrr.de" "button 2|http://rrr.de")" + +# change buttons with help of _button_row +edit_inline_keyboard "${CHAT[ID]}" "567" "$(_button_row "Success edit_inline_keyboard|http://rrr.de")" + +# delete button by replace whole message +edit_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message inline *removed*..." + +``` + ---- + ### Edit / Replace Messages Edit a message means replace the content of the message in place. The message stay on the same position in the chat and keep the same @@ -270,7 +479,7 @@ To replace a message you must know the message id of the the original message. T ##### edit_normal_message `edit_normal_message` replace a message with a text message in the given chat. -*usage:* edit_normal_message "${CHAT[ID]}" "MESSAGE-ID" "message" +*usage:* edit_normal_message "CHAT[ID]" "MESSAGE-ID" "message" *example:* ```bash @@ -283,7 +492,7 @@ edit_normal_message "${CHAT[ID]}" "${saved-id}" "this is another text" ##### edit_markdownv2_message `edit_markdownv2_message` replace a message with a markdown v2 message in the given chat. -*usage:* edit_markdownv2_message "${CHAT[ID]}" "MESSAGE-ID" "message" +*usage:* edit_markdownv2_message "CHAT[ID]" "MESSAGE-ID" "message" *example:* ```bash @@ -296,7 +505,7 @@ edit_markdownv2_message "${CHAT[ID]}" "${saved-id}" "this is __markdown__ *V2* t ##### edit_markdown_message `edit_markdown_message` replace a message with a markdown message in the given chat. -*usage:* edit_markdown_message "${CHAT[ID]}" "MESSAGE-ID" "message" +*usage:* edit_markdown_message "CHAT[ID]" "MESSAGE-ID" "message" *example:* ```bash @@ -309,7 +518,7 @@ edit_markdown_message "${CHAT[ID]}" "${saved-id}" "this is *markdown* text" ##### edit_html_message `edit_html_message` replace a message with a html message in the given chat. -*usage:* edit_html_message "${CHAT[ID]}" "MESSAGE-ID" "message" +*usage:* edit_html_message "CHAT[ID]" "MESSAGE-ID" "message" *example:* ```bash @@ -322,7 +531,7 @@ edit_html_message "${CHAT[ID]}" "${saved-id}" "this is html text" ##### edit_message_caption `edit_message_caption` changes the caption of a message (photo, audio, video, document) in the given chat. -*usage:* edit_message_caption "${CHAT[ID]}" "MESSAGE-ID" "caption" +*usage:* edit_message_caption "CHAT[ID]" "MESSAGE-ID" "caption" --- @@ -330,55 +539,60 @@ edit_html_message "${CHAT[ID]}" "${saved-id}" "this is html text" ### Manage Group To use the following functions the bot must have administrator status in the chat / group +##### chat_member_count +`chat_member_count` returns (putput) number of chat members. + +*usage:* num_members="$(chat_member_count "CHAT[ID]")" + ##### set_chat_title `set_chat_title` sets a new chat title. If new title is the same than current title Telegram return error 400 with description "Bad Request: chat title is not modified" -*usage:* set_chat_title "${CHAT[ID]}" "new chat title" +*usage:* set_chat_title "CHAT[ID]" "new chat title" ##### set_chat_description `set_chat_description` sets a new description title. If new description is the same than current description Telegram return error 400 with description "Bad Request: chat description is not modified" -*usage:* set_chat_description "${CHAT[ID]}" "new chat description" +*usage:* set_chat_description "CHAT[ID]" "new chat description" ##### new_chat_invite `new_chat_invite` generate a new invite link for a chat; any previously generated link is revoked. Returns the new invite link as String on success. -*usage:* new_chat_invite "${CHAT[ID]}" +*usage:* new_chat_invite "CHAT[ID]" ##### delete_chat_photo -*usage:* delete_chat_photo "${CHAT[ID]}" +*usage:* delete_chat_photo "CHAT[ID]" ##### pin_chat_message # $1 chat, $2 message_id `pin_chat_message` add a message to the list of pinned messages in a chat. -*usage:* pin_chat_message "${CHAT[ID]}" "message_id" +*usage:* pin_chat_message "CHAT[ID]" "message_id" ##### unpin_chat_message `unpin_chat_message` remove a message from the list of pinned messages in a chat. -*usage:* unpin_chat_message "${CHAT[ID]}" "message_id" +*usage:* unpin_chat_message "CHAT[ID]" "message_id" ##### unpinall_chat_message `unpinall_chat_message` clear the list of pinned messages in a chat. -*usage:* unpinall_chat_message "${CHAT[ID]}" +*usage:* unpinall_chat_message "CHAT[ID]" ##### delete_chat_stickers `delete_chat_stickers` deletes a group sticker set from a supergroup. -*usage:* delete_chat_stickers "${CHAT[ID]}" +*usage:* delete_chat_stickers "CHAT[ID]" ---- @@ -390,21 +604,21 @@ More advanced API functions are currently not implemented in bashbot. ##### kick_chat_member If your Bot is a chat admin he can kick and ban a user. -*usage:* kick_chat_member "${CHAT[ID]}" "${USER[ID]}" +*usage:* kick_chat_member "CHAT[ID]" "USER[ID]" -*alias:* _kick_user "${USER[ID]}" +*alias:* _kick_user "USER[ID]" ##### unban_chat_member If your Bot is a chat admin can unban a kicked user. -*usage:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}" +*usage:* unban_chat_member "CHAT[ID]" "USER[ID]" -*alias:* _unban "${USER[ID]}" +*alias:* _unban "USER[ID]" ##### leave_chat Your Bot will leave the chat. -*usage:* leave_chat "${CHAT[ID]}" +*usage:* leave_chat "CHAT[ID]" *alias:* _leave @@ -417,6 +631,25 @@ fi See also [kick Chat Member](https://core.telegram.org/bots/api/#kickchatmember)* + +##### promote_chat_member +`promote_chat_member` promote or denote user rights in a chat. Bot must be admin and can only promote/denote rights he owns. + +Right are specified as "right:bool" pairs, where right is one of `long` or `short` listed below, followed +by `:true` or `:false`. Anything but `:true` (e.g. nothing or :xyz) is `:false`. + +long: `is_anonymous can_change_info can_post_messages can_edit_messages can_delete_messages can_invite_users can_restrict_members can_pin_messages can_promote_member` +short: `anon change post edit delete invite restrict pin promote` + +*usage:* promote_chat_member "CHAT[ID]" "USER[ID]" "right[:true|false]" ... "right[:true|false]" + +See also [promote Chat Member](https://core.telegram.org/bots/api#promotechatmember)* + +*example:* +```bash +# USER can post, can't edit, can't delete, can't pin message, can invite users +promote_chat_member "CHAT[ID}" "USER[ID]" "post:true" "can_edit_message" "delete:false" "pin:xxx" "invite:true" +``` ---- The following functions are bashbot only and not part of the Telegram API. @@ -424,7 +657,7 @@ The following functions are bashbot only and not part of the Telegram API. ##### bot_is_admin Return true (0) if bot is admin or creator of given chat. -*usage:* bot_is_admin "${CHAT[ID]}" +*usage:* bot_is_admin "CHAT[ID]" *example:* @@ -437,7 +670,7 @@ fi ##### user_is_botadmin Return true (0) if user is admin of bot, user id if botadmin is read from file './botadmin'. -*usage:* user_is_botadmin "${USER[ID]}" +*usage:* user_is_botadmin "USER[ID]" *alias:* _is_botadmin @@ -449,14 +682,14 @@ user_is_botadmin "${CHAT[ID]}" && send_markdown_message "${CHAT[ID]}" "You are * ##### user_is_creator Return true (0) if user is creator of given chat or chat is a private chat. -*usage:* user_is_creator "${CHAT[ID]}" "${USER[ID]}" +*usage:* user_is_creator "CHAT[ID]" "USER[ID]" *alias:* _is_creator ##### user_is_admin Return true (0) if user is admin or creator of given chat. -*usage:* user_is_admin "${CHAT[ID]}" "${USER[ID]}" +*usage:* user_is_admin "CHAT[ID]" "USER[ID]" *alias:* _is_admin @@ -474,7 +707,7 @@ fi `uers_is_allowed` checks if: user id botadmin, user is group admin or user is allowed to execute action.. Allowed actions are configured as User Access Control rules, see [Advanced Usage](3_advanced.md) -*usage:* user_is_allowed "${USER[ID]}" "action" "${CHAT[ID]}" +*usage:* user_is_allowed "USER[ID]" "action" "CHAT[ID]" *example:* ```bash @@ -562,7 +795,7 @@ chats and send messages based on time or other external events. ##### start_proc `startproc` starts a script, the output of the script is sent to the user or chat, user input will be sent back to the script. see [Advanced Usage](3_advanced.md#Interactive-Chats) -*usage:* start_proc "${CHAT[ID]}" "script" +*usage:* start_proc "CHAT[ID]" "script" *alias:* startproc "script" @@ -575,7 +808,7 @@ startproc 'examples/calc.sh' ##### check_proc Return true (0) if an interactive script is running in the chat. -*usage:* check_prog "${CHAT[ID]}" +*usage:* check_prog "CHAT[ID]" *alias:* checkprog @@ -591,7 +824,7 @@ fi ##### kill_proc Kill the interactive script running in the chat -*usage:* kill_proc "${CHAT[ID]}" +*usage:* kill_proc "CHAT[ID]" *alias:* killproc @@ -611,7 +844,7 @@ Starts a script as a background job and attaches a job name to it. All output fr In contrast to interactive chats, background jobs do not receive user input and can run forever. In addition you can suspend and restart running jobs, e.g. after reboot. -*usage:* start_back "${CHAT[ID]}" "script" "jobname" +*usage:* start_back "CHAT[ID]" "script" "jobname" *alias:* background "script" "jobname" @@ -623,7 +856,7 @@ background "examples/notify.sh" "notify" ##### check_back Return true (0) if an background job is active in the given chat. -*usage:* check_back "${CHAT[ID]}" "jobname" +*usage:* check_back "CHAT[ID]" "jobname" *alias:* checkback "jobname" @@ -639,7 +872,7 @@ fi ##### kill_back -*usage:* kill_back "${CHAT[ID]}" "jobname" +*usage:* kill_back "CHAT[ID]" "jobname" *alias:* killback "jobname" @@ -660,7 +893,7 @@ fi `send_interactive` is used to forward messages to interactive jobs. Usually a message is automatically forwarded from within `commands.sh`, but you can send messages yourself. -*usage:* send_interactive "${CHAT[ID]}" "message" +*usage:* send_interactive "CHAT[ID]" "message" ---- @@ -989,6 +1222,91 @@ https://linuxhint.com/associative_array_bash/ https://linuxconfig.org/how-to-use-arrays-in-bash-script +---- + +### Manage webhook +Bashbot default mode is to poll Telegram server for updates but Telegram offers also webhook as a more efficient method to deliver updates. + +*Important*: Before enable webhook you must setup your server to [receive and process webhook updates from Telegram](../examples/webhook) +I recommend to use webhook with a test bot first. + +##### get_webhook_info +`get_webhook_info` get current status of webhook for your bot, e.g. url, waiting updates, last error. + +*usage:* get_webhook_info + +*example:* +```bash +bin/any_command.sh get_webhook_info + +["URL"] "" +["OK"] "true" +["LASTERR"] "" +["COUNT"] "0" +["CERT"] "false" +["result","pending_update_count"] "0" +["ok"] "true" +["result","has_custom_certificate"] "false" +``` + + +##### delete_webhook +`delete_webhook` deletes your bots current webhook, deletes outstanding updates also if second arg is `true` + +*usage:* delete_webhook [true|false] + +*example:* + +```bash +bin/any_command.sh delete_webhook false + +["RESULT"] "true" +["OK"] "true" +["result"] "true" +["ok"] "true" +["description"] "Webhook was deleted" +``` + + +##### set_webhook +`set_webhook` instructs Telegram to use your bots webhook for delivering updates. If webhook is set +it's no more possible to pull updates from `bashbot start`, you must delete webhook first. + +*Important*: Before using webhook you must setup your server to receive and process updates from Telegram! + +*usage:* set_webhook "https://host.dom[:port][/path]" [max_conn] + +First arg is webhook URL used to send updates to your bot, `:port` and `/path` are optional. +If `:port` is given it must be one of `:443`, `:80`, `:88` or `:8443`, default is`:80`. +For security reasons `BOTTOKEN` will be added to URL (_e.g. `https://myhost.com` -> `https://myhost.com/12345678:azndfhbgdfbbbdsfg/`_). + +Second arg is max connection rate in the range 1-100, bashbot default is 1. + +*example:* + +```bash +bin/any_command.sh set_webhook "https://myhost.com/telegram" "2" + +["OK"] "true" +["RESULT"] "true" +["ok"] "true" +["result"] "true" +["description"] "Webhook is set" + +bin/any_command.sh get_webhook_info + +["OK"] "true" +["URL"] "https://myhost.com/telegram/12345678:AABBCCDDEE...aabbccee124567890/" +["COUNT"] "0" +["CERT"] "false" +["ok"] "true" +["result","ip_address"] "1.2.3.4" +["result","url"] "https://myhost.com/telegram/12345678:AABBCCDDEE...aabbccee124567890/" +["result","pending_update_count"] "0" +["result","max_connections"] "2" +["result","has_custom_certificate"] "false" +``` + ---- ### Aliases - shortcuts for often used functions @@ -1017,13 +1335,13 @@ Do not use them in other files e.g. `bashbot.sh`, modules, addons etc. ##### _kick_user -*usage:* _kick_user "${USER[ID]}" +*usage:* _kick_user "USER[ID]" *alias for:* kick_chat_member "${CHAT[ID]}" "${USER[ID]}" ##### _unban -*usage:* _unban "${USER[ID]}" +*usage:* _unban "USER[ID]" *alias for:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}" @@ -1061,16 +1379,6 @@ Do not use them in other files e.g. `bashbot.sh`, modules, addons etc. ---- -#### _inline_button -*usage:* _inline_button "${1}" "${2}" - -*alias for:* send_inline_button "${CHAT[ID]}" "" "${1}" "${2}" - -#### _inline_keyboard -*usage:* _inline_keyboard "${1}" - -*alias for:* _inline_keyboard "${CHAT[ID]}" "" "${1}" - #### _keyboard_numpad *usage:* _keyboard_numpad @@ -1199,7 +1507,7 @@ killallproc ---- ##### get_file -*usage:* url="$(get_file "${CHAT[ID]}" "message")" +*usage:* url="$(get_file "CHAT[ID]" "message")" ---- @@ -1233,7 +1541,7 @@ Output ARRAY as JSON.sh style data to STDOUT ---- ##### get_chat_member_status -*usage:* get_chat_member_status "${CHAT[ID]}" "${USER[ID]}" +*usage:* get_chat_member_status "CHAT[ID]" "USER[ID]" ---- @@ -1266,8 +1574,9 @@ The name of your bot is available as bash variable "$ME", there is no need to ca *usage:* ME="$(getBotName)" + #### [Prev Best Practice](5_practice.md) #### [Next Notes for Developers](7_develop.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/doc/7_develop.md b/doc/7_develop.md index ec96dc5..e49713e 100644 --- a/doc/7_develop.md +++ b/doc/7_develop.md @@ -284,8 +284,9 @@ bar="${_/__x/_c}" # a_b_c : ${SOMEVAR} # "String in var" $_ -> "var" : $(<"file") # "Content of\n file" $_ -> "file" -# pitfall test command -[ -n "$MYVAR" ] && echo "$_" # outputs "]" +# pitfall [ vs. test command +[ -n "xxx" ] && echo "$_" # $_ -> "]" +test -n "xxx" && echo "$_" # $_ -> "xxx" # pitfall command substitution: globbing and IFS is applied! : "$(echo "a* is born")"# $_ -> a* is globbed even quoted! @@ -386,5 +387,5 @@ fi #### [Prev Function Reference](6_reference.md) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/examples/README.md b/examples/README.md index 1e89153..27a6820 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,6 +56,10 @@ convert existing bots. **jsonDB-keybords** contains a stripped down real world example from my bot showing the usage of jsonDB to store and retrieve values plus use of keyboards in private chats. It's an extended version of mycommands.sh.dist. Messages and help are in german. -#### $$VERSION$$ v1.30-0-g3266427 +### Webhook + +**Webhook** contains instructions on how use webhook API to get updates from telegram instead polling Telegram server. + +#### $$VERSION$$ v1.40-0-gf9dab50 diff --git a/examples/background-scripts/run_diskusage.sh b/examples/background-scripts/run_diskusage.sh index f5a5fbc..6fe2918 100755 --- a/examples/background-scripts/run_diskusage.sh +++ b/examples/background-scripts/run_diskusage.sh @@ -4,7 +4,7 @@ # # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ###### # parameters diff --git a/examples/background-scripts/run_filecontent.sh b/examples/background-scripts/run_filecontent.sh index 67e35cb..5a376b1 100755 --- a/examples/background-scripts/run_filecontent.sh +++ b/examples/background-scripts/run_filecontent.sh @@ -2,7 +2,7 @@ # file: run_filename # background job to display content of all new files in WATCHDIR # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ###### # parameters diff --git a/examples/background-scripts/run_filename.sh b/examples/background-scripts/run_filename.sh index 0ade91d..7c00669 100755 --- a/examples/background-scripts/run_filename.sh +++ b/examples/background-scripts/run_filename.sh @@ -2,7 +2,7 @@ # file: run_filename # background job to display all new files in WATCHDIR # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ###### # parameters diff --git a/examples/background-scripts/run_notify.sh b/examples/background-scripts/run_notify.sh index b27cb64..8c80b27 100755 --- a/examples/background-scripts/run_notify.sh +++ b/examples/background-scripts/run_notify.sh @@ -4,7 +4,7 @@ # # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ###### # parameters diff --git a/examples/bash2env.sh b/examples/bash2env.sh index b7972ae..24cff9d 100755 --- a/examples/bash2env.sh +++ b/examples/bash2env.sh @@ -6,7 +6,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # shellcheck disable=SC1117 -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # adjust your language setting here # https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment diff --git a/examples/bashbot-multi.sh b/examples/bashbot-multi.sh index c6fa3aa..2cef818 100755 --- a/examples/bashbot-multi.sh +++ b/examples/bashbot-multi.sh @@ -2,7 +2,7 @@ # file. multibot.sh # description: run multiple telegram bots from one installation # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 if [ "$2" = "" ] || [ "$2" = "-h" ]; then echo "Usage: $0 botname command" diff --git a/examples/bashbot.cron b/examples/bashbot.cron index 0a12049..f267f99 100644 --- a/examples/bashbot.cron +++ b/examples/bashbot.cron @@ -7,7 +7,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 SHELL=/bin/sh diff --git a/examples/calc.sh b/examples/calc.sh index 7a1ce99..7bfdd73 100755 --- a/examples/calc.sh +++ b/examples/calc.sh @@ -11,7 +11,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ######################################################################## ###### diff --git a/examples/jsonDB-keyboard/mycommands.sh b/examples/jsonDB-keyboard/mycommands.sh index e1739ab..d181903 100644 --- a/examples/jsonDB-keyboard/mycommands.sh +++ b/examples/jsonDB-keyboard/mycommands.sh @@ -10,7 +10,7 @@ # AUTHOR: KayM (), kay@rrr.de # DATE: 19.12.2020 19:03 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # shellcheck disable=SC2154 # shellcheck disable=SC2034 diff --git a/examples/notify.sh b/examples/notify.sh index b709668..9f84492 100755 --- a/examples/notify.sh +++ b/examples/notify.sh @@ -13,7 +13,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ######################################################################## ###### diff --git a/examples/question.sh b/examples/question.sh index 8b116a3..0394bf9 100755 --- a/examples/question.sh +++ b/examples/question.sh @@ -10,7 +10,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ######################################################################## ###### diff --git a/examples/send-system-status/botacl b/examples/send-system-status/botacl index 8fcce22..34ae384 100644 --- a/examples/send-system-status/botacl +++ b/examples/send-system-status/botacl @@ -1,7 +1,7 @@ # file: botacl # a user not listed here, will return false from 'user_is_allowed' # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # Format: # user:resource:chat diff --git a/examples/send-system-status/mycommands.sh b/examples/send-system-status/mycommands.sh index 661497b..942f608 100644 --- a/examples/send-system-status/mycommands.sh +++ b/examples/send-system-status/mycommands.sh @@ -5,7 +5,7 @@ # to show how you can customize bashbot by only editing mycommands.sh # NOTE: this is not tested, simply copied from original source and reworked! # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # # shellcheck disable=SC2154 # shellcheck disable=SC2034 diff --git a/examples/webhook/BASHBOT_HOME b/examples/webhook/BASHBOT_HOME new file mode 100644 index 0000000..778bb33 --- /dev/null +++ b/examples/webhook/BASHBOT_HOME @@ -0,0 +1,3 @@ +/usr/local/github/telegram-bot-bash-develop/DIST/telegram-bot-bash +/usr/local/github/telegram-bot-bash-develop/STANDALONE +/usr/local/telegram-bot-bash diff --git a/examples/webhook/README.md b/examples/webhook/README.md new file mode 100644 index 0000000..bae4124 --- /dev/null +++ b/examples/webhook/README.md @@ -0,0 +1,80 @@ +#### [Examples](../README.md) + +## Bashtbot webhook example + +### Webhooks + +Bashbot default mode is to poll Telegram server for updates but Telegram offers also webhook +as a more efficient method to deliver updates. +If your server is reachable from the Internet you can use the webhook method described here. + + +#### Setup Apache webhook + +Prerequisite: An Apache webserver with a valid SLL certificate chain and php enabled. + +Prepare Apache to forward webhook to Bashbot: + +- install bashbot as described in [Bashbot Installation](../../doc/0_install.md) +- create file `data-bot-bash/webhook-fifo` +- run `bashbot.sh init` to setup bashbot to run as same user as Apache (_e.g. www_) +- go to apache web root and create directory `telegram/` +- copy all files from `examples/webhook` to new directory and change to it +- write bashbot installation directory as first line to file `BASHBOT_HOME` +- execute `php index.php` + +Every call to webhook `https:///telegram//` will execute +`index.php` and write received JSON to file `data-bot-bash/webhook-fifo`. +E.g. the URL `https:///telegram//?json={"test":"me"}` +will append `{"test":"me"}` to the file `data-bot-bash/webhook-fifo`. + +Now your Apache is ready to forward data to Bashbot. + + +#### Simple update processing + +To configure `Simple update processing` delete the file `data-bot-bash/webhook-fifo` after your webhook is working. +All webhook calls are now forwarded to `bin/process_update.sh` for processing. + +To start `Simple processing ` enable webhook on Telegram (_see below_). + +Every incoming Telegram update load Bashbot once for processing one command. Even it seems overkill to load +Bashbot on every incoming update, it's more responsive and create less server load for low traffic bots. + +If your bot uses `addons` or `BASHBOT_EVENTs` you can't use `Simple processing`. + +*Note:* `Simple processing` works without running `bashbot.sh start`. + + +#### High traffic processing + +#### CURRENTLY NOT IMPLEMENTED + +High traffic processing writes Telegram updates to the named pipe `data-bot-bash/webhook-fifo` +and Bashbot poll them, this is much more efficient than polling Telegram server. + +To switch from `Simple processing` to `High traffic processing` start bashbot as `bashbot.sh start-webhook`. +Stop bashbot with `bashbot.sh stop` to switch back to `Simple processing` + + +#### Enable webhook on Telegram + +To get updates via webhook your server must be reachable from the internet and you must +instruct Telegram where to deliver updates, this is done by calling bashbot function `set_webhook`. + +*Example:* + +```bash +bin/any_command.sh set_webhook "https://myserver.com/telegram" +``` + +instruct Telegram to use the URL `https://myserver.com/telegram//` to deliver updates. +After you enable webhook to deliver Telegram updates it's no more possible to poll updates with `bashbot.sh start`. + +To stop delivering of Telegram updates via webhook run `bin/any_command.sh delete_webhook`. + +**Important**: Only https connections with a valid certificate chain are allowed as endpoint for webhook. + + +#### $$VERSION$$ v1.40-0-gf9dab50 + diff --git a/examples/webhook/index.php b/examples/webhook/index.php new file mode 100644 index 0000000..e7d97bf --- /dev/null +++ b/examples/webhook/index.php @@ -0,0 +1,96 @@ += 20 && strpos($tmp, '/.') === false) { + $BASHBOT_HOME=$tmp; + } + } + + // script endpoint + $cmd=$BASHBOT_HOME.'/bin/process_update.sh'; + // fifo endpoint + $fifo=$BASHBOT_HOME.'/data-bot-bash/webhook-fifo'; + + // prepeare read, e.g. run from CLI + $data=''; + $input="php://input"; + $json_file="json.txt"; + if (php_sapi_name() == "cli") { + if(is_readable($json_file)) { + $input=$json_file; + } else { + $input="php://stdin"; + } + } + // read request data + if($json = file_get_contents($input)) { + $data = $json; + } else { + $data = implode(" ",$_POST); + if ($data == '') { $data = implode(" ",$_GET); } + } + // uncomment to save last received JSON + // file_put_contents($json_file, $data); + + // prepare for writing + if ($data == '') { + error_response(400, "No data received"); + } + if (! chdir($BASHBOT_HOME)) { + error_response(403, "No route to bot home"); + } + + // fifo or command? + if (! is_writeable($fifo)) { + // pipe to command + if (! file_exists($cmd)) { + error_response(502, "Webhook endpoint not found"); + } + if (! $handle = popen( $cmd.' debug', 'w' )) { + error_response(503, "Can't open webhook command endpoint"); + } + } else { + // write to fifo + if (! $handle = fopen( $fifo, 'a' )) { + error_response(503, "Can't open webhook file endpoint"); + } + flock($handle, LOCK_EX); + } + if (fwrite( $handle, str_replace(array("\n", "\r"), '',$data). PHP_EOL) === false) { + error_response(504, "Write to webhook failed"); + } + flock($handle, LOCK_UN); + pclose($handle); +/**/ + + function error_response($code, $msg) { + $api = substr(php_sapi_name(), 0, 3); + if ($api == 'cgi' || $api == 'fpm') { + header('Status: '.$code.' '.$msg); + } else { + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'; + header($protocol.' '.$code.' '.$msg); + } + exit('Error '.$code.': '.$msg. PHP_EOL); + } +?> diff --git a/examples/webhook/json.txt b/examples/webhook/json.txt new file mode 100644 index 0000000..83e85cb --- /dev/null +++ b/examples/webhook/json.txt @@ -0,0 +1,2 @@ +{"update_id":665220889, +"message":{"message_id":760,"from":{"id":586928566,"is_bot":false,"first_name":"Kay","last_name":"M","username":"KayM","language_code":"de"},"chat":{"id":589682731,"first_name":"Kay","last_name":"M","username":"KayM","type":"private"},"date":1612029749,"text":"/info","entities":[{"offset":0,"length":5,"type":"bot_command"}]}} diff --git a/modules/aliases.sh b/modules/aliases.sh index 0e551a7..ee6c3a8 100644 --- a/modules/aliases.sh +++ b/modules/aliases.sh @@ -1,11 +1,11 @@ #!/bin/bash -# file: modules/alaises.sh +# file: modules/aliases.sh # do not edit, this file will be overwritten on update # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # # will be automatically sourced from bashbot diff --git a/modules/answerInline.sh b/modules/answerInline.sh index 76ffb64..61bb781 100644 --- a/modules/answerInline.sh +++ b/modules/answerInline.sh @@ -5,7 +5,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # will be automatically sourced from bashbot @@ -31,70 +31,70 @@ inline_query_compose(){ # title2Json title caption description markup inlinekeyboard case "$2" in # user provided media - "article"|"message") # article ID title message (markup description) + "article"|"message") # article ID title message (markup description) JSON='{"type":"article","id":"'${ID}'","input_message_content": {"message_text":"'$4'"} '$(title2Json "$3" "" "$5" "$6" "$7")'}' ;; - "photo") # photo ID photoURL (thumbURL title description caption) + "photo") # photo ID photoURL (thumbURL title description caption) [ -z "$4" ] && tumb="$3" JSON='{"type":"photo","id":"'${ID}'","photo_url":"'$3'","thumb_url":"'$4${tumb}'"'$(title2Json "$5" "$7" "$6" "$7" "$8")'}' ;; - "gif") # gif ID photoURL (thumbURL title caption) + "gif") # gif ID photoURL (thumbURL title caption) [ -z "$4" ] && tumb="$3" JSON='{"type":"gif","id":"'${ID}'","gif_url":"'$3'", "thumb_url":"'$4${tumb}'"'$(title2Json "$5" "$6" "$7" "$8" "$9")'}' ;; - "mpeg4_gif") # mpeg4_gif ID mpegURL (thumbURL title caption) + "mpeg4_gif") # mpeg4_gif ID mpegURL (thumbURL title caption) [ -n "$4" ] && tumb='","thumb_url":"'$4'"' JSON='{"type":"mpeg4_gif","id":"'${ID}'","mpeg4_url":"'$3'"'${tumb}$(title2Json "$5" "$6" "" "$7" "$8")'}' ;; - "video") # video ID videoURL mime thumbURL title (caption) + "video") # video ID videoURL mime thumbURL title (caption) JSON='{"type":"video","id":"'${ID}'","video_url":"'$3'","mime_type":"'$4'","thumb_url":"'$5'"'$(title2Json "$6" "$7" "$8" "$9" "${10}")'}' ;; - "audio") # audio ID audioURL title (caption) + "audio") # audio ID audioURL title (caption) JSON='{"type":"audio","id":"'${ID}'","audio_url":"'$3'"'$(title2Json "$4" "$5" "" "" "$6")'}' ;; - "voice") # voice ID voiceURL title (caption) + "voice") # voice ID voiceURL title (caption) JSON='{"type":"voice","id":"'${ID}'","voice_url":"'$3'"'$(title2Json "$4" "$5" "" "" "$6")'}' ;; - "document") # document ID title documentURL mimetype (caption description) + "document") # document ID title documentURL mimetype (caption description) JSON='{"type":"document","id":"'${ID}'","document_url":"'$4'","mime_type":"'$5'"'$(title2Json "$3" "$6" "$7" "$8" "$9")'}' ;; - "location") # location ID lat long title + "location") # location ID lat long title JSON='{"type":"location","id":"'${ID}'","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}' ;; - "venue") # venue ID lat long title (address forsquare) + "venue") # venue ID lat long title (address forsquare) [ -z "$6" ] && addr="$5" [ -n "$7" ] && fours=',"foursquare_id":"'$7'"' JSON='{"type":"venue","id":"'${ID}'","latitude":"'$3'","longitude":"'$4'","title":"'$5'","address":"'$6${addr}'"'${fours}'}' ;; - "contact") # contact ID phone first (last thumb) + "contact") # contact ID phone first (last thumb) [ -n "$5" ] && last=',"last_name":"'$5'"' [ -n "$6" ] && tumb='","thumb_url":"'$6'"' JSON='{"type":"contact","id":"'${ID}'","phone_number":"'$3'","first_name":"'$4'"'${last}'"}' ;; # title2Json title caption description markup inlinekeyboard # Cached media stored in Telegram server - "cached_photo") # photo ID file (title description caption) + "cached_photo") # photo ID file (title description caption) JSON='{"type":"photo","id":"'${ID}'","photo_file_id":"'$3'"'$(title2Json "$4" "$6" "$5" "$7" "$8")'}' ;; - "cached_gif") # gif ID file (title caption) + "cached_gif") # gif ID file (title caption) JSON='{"type":"gif","id":"'${ID}'","gif_file_id":"'$3'"'$(title2Json "$4" "$5" "$6" "$7" "$8" )'}' ;; - "cached_mpeg4_gif") # mpeg ID file (title caption) + "cached_mpeg4_gif") # mpeg ID file (title caption) JSON='{"type":"mpeg4_gif","id":"'${ID}'","mpeg4_file_id":"'$3'"'$(title2Json "$4" "$5" "" "$6" "$7")'}' ;; - "cached_sticker") # sticker ID file + "cached_sticker") # sticker ID file JSON='{"type":"sticker","id":"'${ID}'","sticker_file_id":"'$3'"}' ;; - "cached_document") # document ID title file (description caption) + "cached_document") # document ID title file (description caption) JSON='{"type":"document","id":"'${ID}'","document_file_id":"'$4'"'$(title2Json "$3" "$6" "$5" "$6" "$7")'}' ;; - "cached_video") # video ID file title (description caption) + "cached_video") # video ID file title (description caption) JSON='{"type":"video","id":"'${ID}'","video_file_id":"'$3'"'$(title2Json "$4" "$6" "$5" "$7" "$8")'}' ;; - "cached_voice") # voice ID file title (caption) + "cached_voice") # voice ID file title (caption) JSON='{"type":"voice","id":"'${ID}'","voice_file_id":"'$3'"'$(title2Json "$4" "$5" "" "" "$6")'}' ;; - "cached_audio") # audio ID file title (caption) + "cached_audio") # audio ID file title (caption) JSON='{"type":"audio","id":"'${ID}'","audio_file_id":"'$3'"'$(title2Json "$4" "$5" "" "" "$6")'}' ;; esac diff --git a/modules/background.sh b/modules/background.sh index 4409616..74f5349 100644 --- a/modules/background.sh +++ b/modules/background.sh @@ -6,7 +6,7 @@ # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # # shellcheck disable=SC1117,SC2059 -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # will be automatically sourced from bashbot @@ -48,10 +48,10 @@ start_back() { } restart_back() { local fifo; fifo="${DATADIR:-.}/$(procname "$1" "back-$3-")" - log_message "Start background job CHAT=$1 JOB=${fifo##*/} CMD=${2##*/} $4 $5" + log_update "Start background job CHAT=$1 JOB=${fifo##*/} CMD=${2##*/} $4 $5" check_back "$1" "$3" && kill_proc "$1" "back-$3-" nohup bash -c "{ $2 \"$4\" \"$5\" \"${fifo}\" | \"${SCRIPT}\" outproc \"$1\" \"${fifo}\"; }" &>>"${fifo}.log" & - sleep 0.5 # give bg job some time to init + sleep 0.5 # give bg job some time to init } @@ -62,7 +62,7 @@ start_proc() { [ -z "$2" ] && return [ -x "${2%% *}" ] || return 1 local fifo; fifo="${DATADIR:-.}/$(procname "$1")" - log_message "Start interactive script CHAT=$1 JOB=${fifo##*/} CMD=$2 $3 $4" + log_update "Start interactive script CHAT=$1 JOB=${fifo##*/} CMD=$2 $3 $4" check_proc "$1" && kill_proc "$1" mkfifo "${fifo}" nohup bash -c "{ $2 \"$4\" \"$5\" \"${fifo}\" | \"${SCRIPT}\" outproc \"$1\" \"${fifo}\" @@ -99,7 +99,7 @@ kill_proc() { fifo="$(procname "$1" "$2")" prid="$(proclist "${fifo}")" fifo="${DATADIR:-.}/${fifo}" - log_message "Stop interactive / background CHAT=$1 JOB=${fifo##*/}" + log_update "Stop interactive / background CHAT=$1 JOB=${fifo##*/}" # shellcheck disable=SC2086 [ -n "${prid}" ] && kill ${prid} [ -s "${fifo}.log" ] || rm -f "${fifo}.log" @@ -110,7 +110,7 @@ kill_proc() { # $2 message send_interactive() { local fifo; fifo="${DATADIR:-.}/$(procname "$1")" - [ -p "${fifo}" ] && printf '%s\n' "$2" >"${fifo}" & # not blocking! + [ -p "${fifo}" ] && printf '%s\n' "$2" >"${fifo}" & # not blocking! } # old style but may not work because of local checks @@ -154,7 +154,7 @@ job_control() { "killb"*) printf "Kill Job: %s %s\n" "${proc}" " ${fifo##*/}" kill_proc "${CHAT}" "${job}" - rm -f "${FILE}" # remove job + rm -f "${FILE}" # remove job # inform botadmin about stop [ -n "${ADM}" ] && send_normal_message "${ADM}" "Bot ${BOT} kill background jobs ..." & killall="y" diff --git a/modules/chatMember.sh b/modules/chatMember.sh index 2f9a316..ddaf992 100644 --- a/modules/chatMember.sh +++ b/modules/chatMember.sh @@ -5,7 +5,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # will be automatically sourced from bashbot @@ -56,6 +56,12 @@ delete_chat_stickers() { } # manage chat member functions ------- +# $1 chat +chat_member_count() { + sendJson "$1" "" "${URL}/getChatMembersCount" + [ "${BOTSENT[OK]}" = "true" ] && printf "%s\n" "${BOTSENT[RESULT]}" +} + kick_chat_member() { sendJson "$1" 'user_id: '"$2"'' "${URL}/kickChatMember" } @@ -68,6 +74,34 @@ leave_chat() { sendJson "$1" "" "${URL}/leaveChat" } +# $1 chat, $2 userid, $3 ... "right[:true]" default false +# right: is_anonymous change_info post_messages edit_messages delete_messages invite_users restrict_members pin_messages promote_member +promote_chat_member() { + local arg bool json chat="$1" user="$2; shift 2" + for arg in "$@" + do + # default false + bool=false; [ "${arg##*:}" = "true" ] && bool="true" + # expand args + case "${arg}" in + *"anon"*) arg="is_anonymous";; + *"change"*) arg="can_change_info";; + *"post"*) arg="can_post_messages";; + *"edit"*) arg="can_edit_messages";; + *"delete"*) arg="can_delete_messages";; + *"pin"*) arg="can_pin_messages";; + *"invite"*) arg="can_invite_users";; + *"restrict"*) arg="can_restrict_members";; + *"promote"*) arg="can_promote_members";; + *) [ -n "${BASHBOTDEBUG}" ] && debug_log "${FUNCNAME[0]}: unknown promotion ${arg}" + continue;; + esac + # compose json + [ -n "${json}" ] && json+="," + json+='"'"${arg}"'": "'"${bool}"'"' + done + sendJson "${chat}" '"user_id":'"${user}"','"${json}"'' "${URL}/promoteChatMember" +} # bashbot specific functions --------- @@ -76,7 +110,7 @@ leave_chat() { get_chat_member_status() { sendJson "$1" '"user_id":'"$2"'' "${URL}/getChatMember" # shellcheck disable=SC2154 - JsonGetString '"result","status"' <<< "${res}" + printf "%s\n" "${UPD["result,status"]}" } user_is_creator() { diff --git a/modules/jsonDB.sh b/modules/jsonDB.sh index 9c49c3c..dfaec7d 100644 --- a/modules/jsonDB.sh +++ b/modules/jsonDB.sh @@ -5,7 +5,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # # source from commands.sh to use jsonDB functions # @@ -42,7 +42,7 @@ JSSH_KEYOK="[-${azAZo9},._]" # $1 - invalid charcaters are replaced with first character # or deleted if $1 is empty jssh_stripKey() { # tr: we must escape first - in [-a-z...] - if [[ "$1" =~ ^${JSSH_KEYOK} ]]; then # tr needs [\-... + if [[ "$1" =~ ^${JSSH_KEYOK} ]]; then # tr needs [\-... tr -c "${JSSH_KEYOK/\[-/[\\-}\r\n" "${1:0:1}" else tr -dc "${JSSH_KEYOK/\[-/[\\-}\r\n" @@ -86,7 +86,7 @@ if [ "$(LC_ALL=C type -t "flock")" = "file" ]; then jssh_updateDB() { # for atomic update we can't use read/writeDB [ -z "$2" ] && return 1 - local DB="$2.jssh" # check in async + local DB="$2.jssh" # check in async [ ! -f "${DB}" ] && return 2 { flock -e -w 10 200; jssh_updateDB_async "$@"; } 200>"${DB}${JSSH_LOCKNAME}" } @@ -95,7 +95,7 @@ if [ "$(LC_ALL=C type -t "flock")" = "file" ]; then # $1 key name, can only contain -a-zA-Z0-9,._ # $2 key value # $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' - alias jssh_insertDB=jssh_insertKeyDB # backward compatibility + alias jssh_insertDB=jssh_insertKeyDB # backward compatibility # renamed to be more consistent jssh_insertKeyDB() { [[ "$1" =~ ^${JSSH_KEYOK}+$ ]] || return 3 @@ -179,7 +179,7 @@ if [ "$(LC_ALL=C type -t "flock")" = "file" ]; then # medium complex, wrapper async jssh_updateArray() { [ -z "$2" ] && return 1 - local DB="$2.jssh" # name check in async + local DB="$2.jssh" # name check in async [ ! -f "${DB}" ] && return 2 declare -n ARRAY="$1" [[ -z "${ARRAY[*]}" || "${DB}" -nt "${DB}.last$3" ]] && touch "${DB}.last$3" && jssh_readDB "$1" "$2" @@ -216,7 +216,7 @@ jssh_newDB_async() { jssh_newDB "$@"; } jssh_newDB() { local DB; DB="$(jssh_checkDB "$1")" [ -z "${DB}" ] && return 1 - [ -f "${DB}" ] && return 2 # already exist + [ -f "${DB}" ] && return 2 # already exist touch "${DB}" } diff --git a/modules/processUpdates.sh b/modules/processUpdates.sh new file mode 100644 index 0000000..cb422e8 --- /dev/null +++ b/modules/processUpdates.sh @@ -0,0 +1,496 @@ +#!/bin/bash +################################################################## +# +# File: processUpdates.sh +# Note: DO NOT EDIT! this file will be overwritten on update +# +#### $$VERSION$$ v1.40-0-gf9dab50 +################################################################## + +############## +# manage webhooks + +# $1 URL to sed updates to: https://host.dom[:port][/path], port and path are optional +# port must be 443, 80, 88 8443, TOKEN will be added to URL for security +# e.g. https://myhost.com -> https://myhost.com/12345678:azndfhbgdfbbbdsfg +# $2 max connections 1-100 default 1 (because of bash ;-) +set_webhook() { + local url='"url": "'"$1/${BOTTOKEN}/"'"' + local max=',"max_connections": 1' + [[ "$2" =~ ^[0-9]+$ ]] && max=',"max_connections": '"$2"'' + # shellcheck disable=SC2153 + sendJson "" "${url}${max}" "${URL}/setWebhook" + unset "BOTSENT[ID]" "BOTSENT[CHAT]" + +} + +get_webhook_info() { + sendJson "" "" "${URL}/getWebhookInfo" + if [ "${BOTSENT[OK]}" = "true" ]; then + BOTSENT[URL]="${UPD[result,url]}" + BOTSENT[COUNT]="${UPD[result,pending_update_count]}" + BOTSENT[CERT]="${UPD[result,has_custom_certificate]}" + BOTSENT[LASTERR]="${UPD[result,last_error_message]}" + unset "BOTSENT[ID]" "BOTSENT[CHAT]" + fi +} + +# $1 drop pending updates true/false, default false +delete_webhook() { + local drop; [ "$1" = "true" ] && drop='"drop_pending_updates": true' + sendJson "" "${drop}" "${URL}/deleteWebhook" + unset "BOTSENT[ID]" "BOTSENT[CHAT]" +} + +################ +# processing of updates starts here +process_multi_updates() { + local max num debug="$1" + max="$(grep -F ',"update_id"]' <<< "${UPDATE}" | tail -1 | cut -d , -f 2 )" + Json2Array 'UPD' <<<"${UPDATE}" + for ((num=0; num<=max; num++)); do + process_update "${num}" "${debug}" + done +} + +process_update() { + local num="$1" debug="$2" + pre_process_message "${num}" + # log message on debug + [[ -n "${debug}" ]] && log_message "New Message ==========\n$(grep -F '["result",'"${num}" <<<"${UPDATE}")" + + # check for users / groups to ignore + jssh_updateArray_async "BASHBOTBLOCKED" "${BLOCKEDFILE}" + [ -n "${USER[ID]}" ] && [[ -n "${BASHBOTBLOCKED[${USER[ID]}]}" || -n "${BASHBOTBLOCKED[${CHAT[ID]}]}" ]] && return + + # process per message type + if [ -n "${iQUERY[ID]}" ]; then + process_inline_query "${num}" "${debug}" + printf "%(%c)T: Inline Query update received FROM=%s iQUERY=%s\n" -1\ + "${iQUERY[USERNAME]:0:20} (${iQUERY[USER_ID]})" "${iQUERY[0]}" >>"${UPDATELOG}" + elif [ -n "${iBUTTON[ID]}" ]; then + process_inline_button "${num}" "${debug}" + printf "%(%c)T: Inline Button update received FROM=%s CHAT=%s CALLBACK=%s DATA:%s \n" -1\ + "${iBUTTON[USERNAME]:0:20} (${iBUTTON[USER_ID]})" "${iBUTTON[CHAT_ID]}" "${iBUTTON[ID]}" "${iBUTTON[DATA]}" >>"${UPDATELOG}" + else + if grep -qs -e '\["result",'"${num}"',"edited_message"' <<<"${UPDATE}"; then + # edited message + UPDATE="${UPDATE//,${num},\"edited_message\",/,${num},\"message\",}" + Json2Array 'UPD' <<<"${UPDATE}" + MESSAGE[0]="/_edited_message " + fi + process_message "${num}" "${debug}" + printf "%(%c)T: update received FROM=%s CHAT=%s CMD=%s\n" -1 "${USER[USERNAME]:0:20} (${USER[ID]})"\ + "${CHAT[USERNAME]:0:20}${CHAT[TITLE]:0:30} (${CHAT[ID]})"\ + "${MESSAGE:0:30}${CAPTION:0:30}$(: "${URLS[*]//bot*:}"; printf "%s" "${_//[A-Z-]}")" >>"${UPDATELOG}" + fi + ##### + # process inline and message events + # first classic command dispatcher + # shellcheck disable=SC2153,SC1090 + { source "${COMMANDS}" "${debug}"; } & + + # then all registered addons + if [ -z "${iQUERY[ID]}" ]; then + event_message "${debug}" + else + event_inline "${debug}" + fi + + # last count users + jssh_countKeyDB_async "${CHAT[ID]}" "${COUNTFILE}" +} + +pre_process_message(){ + local num="$1" + # unset everything to not have old values + CMD=( ); iQUERY=( ); iBUTTON=(); MESSAGE=(); CHAT=(); USER=(); CONTACT=(); LOCATION=(); unset CAPTION + REPLYTO=( ); FORWARD=( ); URLS=(); VENUE=( ); SERVICE=( ); NEWMEMBER=( ); LEFTMEMBER=( ); PINNED=( ); MIGRATE=( ) + iQUERY[ID]="${UPD["result,${num},inline_query,id"]}" + iBUTTON[ID]="${UPD["result,${num},callback_query,id"]}" + CHAT[ID]="${UPD["result,${num},message,chat,id"]}" + USER[ID]="${UPD["result,${num},message,from,id"]}" + [ -z "${CHAT[ID]}" ] && CHAT[ID]="${UPD["result,${num},edited_message,chat,id"]}" + [ -z "${USER[ID]}" ] && USER[ID]="${UPD["result,${num},edited_message,from,id"]}" + # always true + return 0 +} + +process_inline_query() { + local num="$1" + iQUERY[0]="$(JsonDecode "${UPD["result,${num},inline_query,query"]}")" + iQUERY[USER_ID]="${UPD["result,${num},inline_query,from,id"]}" + iQUERY[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},inline_query,from,first_name"]}")" + iQUERY[LAST_NAME]="$(JsonDecode "${UPD["result,${num},inline_query,from,last_name"]}")" + iQUERY[USERNAME]="$(JsonDecode "${UPD["result,${num},inline_query,from,username"]}")" + # always true + return 0 +} + +process_inline_button() { +# debugging for impelemetation + local num="$1" + iBUTTON[DATA]="${UPD["result,${num},callback_query,data"]}" + iBUTTON[CHAT_ID]="${UPD["result,${num},callback_query,message,chat,id"]}" + iBUTTON[MESSAGE_ID]="${UPD["result,${num},callback_query,message,message_id"]}" + iBUTTON[MESSAGE]="$(JsonDecode "${UPD["result,${num},callback_query,message,text"]}")" +# XXX should we give back pressed button, all buttons or nothing? + iBUTTON[USER_ID]="${UPD["result,${num},callback_query,from,id"]}" + iBUTTON[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},callback_query,from,first_name"]}")" + iBUTTON[LAST_NAME]="$(JsonDecode "${UPD["result,${num},callback_query,from,last_name"]}")" + iBUTTON[USERNAME]="$(JsonDecode "${UPD["result,${num},callback_query,from,username"]}")" + # always true + return 0 +} + +process_message() { + local num="$1" + # Message + MESSAGE[0]+="$(JsonDecode "${UPD["result,${num},message,text"]}" | sed 's|\\/|/|g')" + MESSAGE[ID]="${UPD["result,${num},message,message_id"]}" + + # Chat ID is now parsed when update is received + CHAT[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,chat,last_name"]}")" + CHAT[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,chat,first_name"]}")" + CHAT[USERNAME]="$(JsonDecode "${UPD["result,${num},message,chat,username"]}")" + # set real name as username if empty + [ -z "${CHAT[USERNAME]}" ] && CHAT[USERNAME]="${CHAT[FIRST_NAME]} ${CHAT[LAST_NAME]}" + CHAT[TITLE]="$(JsonDecode "${UPD["result,${num},message,chat,title"]}")" + CHAT[TYPE]="$(JsonDecode "${UPD["result,${num},message,chat,type"]}")" + CHAT[ALL_ADMIN]="${UPD["result,${num},message,chat,all_members_are_administrators"]}" + + # user ID is now parsed when update is received + USER[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,from,first_name"]}")" + USER[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,from,last_name"]}")" + USER[USERNAME]="$(JsonDecode "${UPD["result,${num},message,from,username"]}")" + # set real name as username if empty + [ -z "${USER[USERNAME]}" ] && USER[USERNAME]="${USER[FIRST_NAME]} ${USER[LAST_NAME]}" + + # in reply to message from + if [ -n "${UPD["result,${num},message,reply_to_message,from,id"]}" ]; then + REPLYTO[UID]="${UPD["result,${num},message,reply_to_message,from,id"]}" + REPLYTO[0]="$(JsonDecode "${UPD["result,${num},message,reply_to_message,text"]}")" + REPLYTO[ID]="${UPD["result,${num},message,reply_to_message,message_id"]}" + REPLYTO[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,reply_to_message,from,first_name"]}")" + REPLYTO[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,reply_to_message,from,last_name"]}")" + REPLYTO[USERNAME]="$(JsonDecode "${UPD["result,${num},message,reply_to_message,from,username"]}")" + fi + + # forwarded message from + if [ -n "${UPD["result,${num},message,forward_from,id"]}" ]; then + FORWARD[UID]="${UPD["result,${num},message,forward_from,id"]}" + FORWARD[ID]="${MESSAGE[ID]}" # same as message ID + FORWARD[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,forward_from,first_name"]}")" + FORWARD[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,forward_from,last_name"]}")" + FORWARD[USERNAME]="$(JsonDecode "${UPD["result,${num},message,forward_from,username"]}")" + fi + + # get file URL from telegram, check for any of them! + if grep -qs -e '\["result",'"${num}"',"message","[avpsd].*,"file_id"\]' <<<"${UPDATE}"; then + URLS[AUDIO]="$(get_file "${UPD["result,${num},message,audio,file_id"]}")" + URLS[DOCUMENT]="$(get_file "${UPD["result,${num},message,document,file_id"]}")" + URLS[PHOTO]="$(get_file "${UPD["result,${num},message,photo,0,file_id"]}")" + URLS[STICKER]="$(get_file "${UPD["result,${num},message,sticker,file_id"]}")" + URLS[VIDEO]="$(get_file "${UPD["result,${num},message,video,file_id"]}")" + URLS[VOICE]="$(get_file "${UPD["result,${num},message,voice,file_id"]}")" + fi + # Contact, must have phone_number + if [ -n "${UPD["result,${num},message,contact,phone_number"]}" ]; then + CONTACT[USER_ID]="$(JsonDecode "${UPD["result,${num},message,contact,user_id"]}")" + CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,contact,first_name"]}")" + CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,contact,last_name"]}")" + CONTACT[NUMBER]="${UPD["result,${num},message,contact,phone_number"]}" + CONTACT[VCARD]="${UPD["result,${num},message,contact,vcard"]}" + fi + + # venue, must have a position + if [ -n "${UPD["result,${num},message,venue,location,longitude"]}" ]; then + VENUE[TITLE]="$(JsonDecode "${UPD["result,${num},message,venue,title"]}")" + VENUE[ADDRESS]="$(JsonDecode "${UPD["result,${num},message,venue,address"]}")" + VENUE[LONGITUDE]="${UPD["result,${num},message,venue,location,longitude"]}" + VENUE[LATITUDE]="${UPD["result,${num},message,venue,location,latitude"]}" + VENUE[FOURSQUARE]="${UPD["result,${num},message,venue,foursquare_id"]}" + fi + + # Caption + CAPTION="$(JsonDecode "${UPD["result,${num},message,caption"]}")" + + # Location + LOCATION[LONGITUDE]="${UPD["result,${num},message,location,longitude"]}" + LOCATION[LATITUDE]="${UPD["result,${num},message,location,latitude"]}" + + # service messages, group or channel only! + if [[ "${CHAT[ID]}" == "-"* ]] ; then + # new chat member + if [ -n "${UPD["result,${num},message,new_chat_member,id"]}" ]; then + SERVICE[NEWMEMBER]="${UPD["result,${num},message,new_chat_member,id"]}" + NEWMEMBER[ID]="${SERVICE[NEWMEMBER]}" + NEWMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,new_chat_member,first_name"]}")" + NEWMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,new_chat_member,last_name"]}")" + NEWMEMBER[USERNAME]="$(JsonDecode "${UPD["result,${num},message,new_chat_member,username"]}")" + NEWMEMBER[ISBOT]="${UPD["result,${num},message,new_chat_member,is_bot"]}" + [ -z "${MESSAGE[0]}" ] &&\ + MESSAGE[0]="/_new_chat_member ${NEWMEMBER[ID]} ${NEWMEMBER[USERNAME]:=${NEWMEMBER[FIRST_NAME]} ${NEWMEMBER[LAST_NAME]}}" + fi + # left chat member + if [ -n "${UPD["result,${num},message,left_chat_member,id"]}" ]; then + SERVICE[LEFTMEMBER]="${UPD["result,${num},message,left_chat_member,id"]}" + LEFTMEMBER[ID]="${SERVICE[LEFTMEBER]}" + LEFTMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result,${num},message,left_chat_member,first_name"]}")" + LEFTMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result,${num},message,left_chat_member,last_name"]}")" + LEFTMEBER[USERNAME]="$(JsonDecode "${UPD["result,${num},message,left_chat_member,username"]}")" + LEFTMEMBER[ISBOT]="${UPD["result,${num},message,left_chat_member,is_bot"]}" + [ -z "${MESSAGE[0]}" ] &&\ + MESSAGE[0]="/_left_chat_member ${LEFTMEMBER[ID]} ${LEFTMEMBER[USERNAME]:=${LEFTMEMBER[FIRST_NAME]} ${LEFTMEMBER[LAST_NAME]}}" + fi + # chat title / photo, check for any of them! + if grep -qs -e '\["result",'"${num}"',"message","new_chat_[tp]' <<<"${UPDATE}"; then + SERVICE[NEWTITLE]="$(JsonDecode "${UPD["result,${num},message,new_chat_title"]}")" + [ -z "${MESSAGE[0]}" ] && [ -n "${SERVICE[NEWTITLE]}" ] &&\ + MESSAGE[0]="/_new_chat_title ${USER[ID]} ${SERVICE[NEWTITLE]}" + SERVICE[NEWPHOTO]="$(get_file "${UPD["result,${num},message,new_chat_photo,0,file_id"]}")" + [ -z "${MESSAGE[0]}" ] && [ -n "${SERVICE[NEWPHOTO]}" ] &&\ + MESSAGE[0]="/_new_chat_photo ${USER[ID]} ${SERVICE[NEWPHOTO]}" + fi + # pinned message + if [ -n "${UPD["result,${num},message,pinned_message,message_id"]}" ]; then + SERVICE[PINNED]="${UPD["result,${num},message,pinned_message,message_id"]}" + PINNED[ID]="${SERVICE[PINNED]}" + PINNED[MESSAGE]="$(JsonDecode "${UPD["result,${num},message,pinned_message,text"]}")" + [ -z "${MESSAGE[0]}" ] &&\ + MESSAGE[0]="/_new_pinned_message ${USER[ID]} ${PINNED[ID]} ${PINNED[MESSAGE]}" + fi + # migrate to super group + if [ -n "${UPD["result,${num},message,migrate_to_chat_id"]}" ]; then + MIGRATE[TO]="${UPD["result,${num},message,migrate_to_chat_id"]}" + MIGRATE[FROM]="${UPD["result,${num},message,migrate_from_chat_id"]}" + # CHAT is already migrated, so set new chat id + [ "${CHAT[ID]}" = "${MIGRATE[FROM]}" ] && CHAT[ID]="${MIGRATE[FROM]}" + SERVICE[MIGRATE]="${MIGRATE[FROM]} ${MIGRATE[TO]}" + [ -z "${MESSAGE[0]}" ] &&\ + MESSAGE[0]="/_migrate_group ${SERVICE[MIGRATE]}" + fi + # set SERVICE to yes if a service message was received + [[ "${SERVICE[*]}" =~ ^[[:blank:]]*$ ]] || SERVICE[0]="yes" + fi + + # split message in command and args + [[ "${MESSAGE[0]}" == "/"* ]] && read -r CMD <<<"${MESSAGE[0]}" && CMD[0]="${CMD[0]%%@*}" + # everything went well + return 0 +} + +######################### +# main get updates loop, should never terminate +declare -A BASHBOTBLOCKED +start_bot() { + local DEBUGMSG + # startup message + DEBUGMSG="Start BASHBOT updates in Mode \"${1:-normal}\" ==========" + log_update "${DEBUGMSG}" + # redirect to Debug.log + # shellcheck disable=SC2153 + [[ "$1" == *"debug" ]] && exec &>>"${DEBUGLOG}" + log_debug "${DEBUGMSG}"; DEBUGMSG="$1" + [[ "${DEBUGMSG}" == "xdebug"* ]] && set -x + # cleaup old pipes and empty logfiles + find "${DATADIR}" -type p -delete + find "${DATADIR}" -size 0 -name "*.log" -delete + # load addons on startup + for addons in "${ADDONDIR:-.}"/*.sh ; do + # shellcheck disable=SC1090 + [ -r "${addons}" ] && source "${addons}" "startbot" "${DEBUGMSG}" + done + # shellcheck disable=SC1090 + source "${COMMANDS}" "startbot" + # start timer events + if [ -n "${BASHBOT_START_TIMER}" ] ; then + # shellcheck disable=SC2064 + trap "event_timer ${DEBUGMSG}" ALRM + start_timer & + # shellcheck disable=SC2064 + trap "kill -9 $!; exit" EXIT INT HUP TERM QUIT + fi + # cleanup countfile on startup + jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" "${COUNTFILE}" + [ -f "${COUNTFILE}.jssh.flock" ] && rm -f "${COUNTFILE}.jssh.flock" + # store start time and cleanup botconfig on startup + jssh_updateKeyDB "startup" "$(_date)" "${BOTCONFIG}" + [ -f "${BOTCONFIG}.jssh.flock" ] && rm -f "${BOTCONFIG}.jssh.flock" + # read blocked users + jssh_readDB_async "BASHBOTBLOCKED" "${BLOCKEDFILE}" + # inform botadmin about start + send_normal_message "$(getConfigKey "botadmin")" "Bot $(getConfigKey "botname") started ..." & + ########## + # bot is ready, start processing updates ... + get_updates "${DEBUGMSG}" +} + + +get_updates(){ + local errsleep="200" DEBUG="$1" OFFSET=0 + # adaptive sleep defaults + local nextsleep="100" + local stepsleep="${BASHBOT_SLEEP_STEP:-100}" + local maxsleep="${BASHBOT_SLEEP:-5000}" + while true; do + # adaptive sleep in ms rounded to next 0.1 s + sleep "$(_round_float "${nextsleep}e-3" "1")" + # get next update + # shellcheck disable=SC2153 + UPDATE="$(getJson "${URL}/getUpdates?offset=${OFFSET}" 2>/dev/null | "${JSONSHFILE}" -b -n 2>/dev/null | iconv -f utf-8 -t utf-8 -c)" + # did we get an response? + if [ -n "${UPDATE}" ]; then + # we got something, do processing + [ "${OFFSET}" = "-999" ] && [ "${nextsleep}" -gt "$((maxsleep*2))" ] &&\ + log_error "Recovered from timeout/broken/no connection, continue with telegram updates" + # calculate next sleep interval + ((nextsleep+= stepsleep , nextsleep= nextsleep>maxsleep ?maxsleep:nextsleep)) + # escape bash $ expansion bug + UPDATE="${UPDATE//$/\\$}" + # warn if webhook is set + if grep -q '^\["error_code"\] 409' <<<"${UPDATE}"; then + [ "${OFFSET}" != "-999" ] && nextsleep="${stepsleep}" + OFFSET="-999"; errsleep="$(_round_float "$(( errsleep= 300*nextsleep ))e-3")" + log_error "Warning conflicting webhook set, can't get updates until your run delete_webhook! Sleep $((errsleep/60)) min ..." + sleep "${errsleep}" + continue + fi + # Offset + OFFSET="$(grep <<<"${UPDATE}" '\["result",[0-9]*,"update_id"\]' | tail -1 | cut -f 2)" + ((OFFSET++)) + + if [ "${OFFSET}" != "1" ]; then + nextsleep="100" + process_multi_updates "${DEBUG}" + fi + else + # oops, something bad happened, wait maxsleep*10 + (( nextsleep=nextsleep*2 , nextsleep= nextsleep>maxsleep*10 ?maxsleep*10:nextsleep )) + # second time, report problem + if [ "${OFFSET}" = "-999" ]; then + log_error "Repeated timeout/broken/no connection on telegram update, sleep $(_round_float "${nextsleep}e-3")s" + # try to recover + if _is_function bashbotBlockRecover && [ -z "$(getJson "${ME_URL}")" ]; then + log_error "Try to recover, calling bashbotBlockRecover ..." + bashbotBlockRecover >>"${ERRORLOG}" + fi + fi + OFFSET="-999" + fi + done +} + + + +declare -Ax BASHBOT_EVENT_INLINE BASHBOT_EVENT_MESSAGE BASHBOT_EVENT_CMD BASHBOT_EVENT_REPLYTO BASHBOT_EVENT_FORWARD BASHBOT_EVENT_SEND +declare -Ax BASHBOT_EVENT_CONTACT BASHBOT_EVENT_LOCATION BASHBOT_EVENT_FILE BASHBOT_EVENT_TEXT BASHBOT_EVENT_TIMER BASHBOT_BLOCKED + +start_timer(){ + # send alarm every ~60 s + while :; do + sleep 59.5 + kill -ALRM $$ + done; +} + +EVENT_TIMER="0" +event_timer() { + local key timer debug="$1" + (( EVENT_TIMER++ )) + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_TIMER[@]}" + do + timer="${key##*,}" + [[ ! "${timer}" =~ ^-*[1-9][0-9]*$ ]] && continue + if [ "$(( EVENT_TIMER % timer ))" = "0" ]; then + _exec_if_function "${BASHBOT_EVENT_TIMER[${key}]}" "timer" "${key}" "${debug}" + [ "$(( EVENT_TIMER % timer ))" -lt "0" ] && \ + unset BASHBOT_EVENT_TIMER["${key}"] + fi + done +} + +event_inline() { + local key debug="$1" + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_INLINE[@]}" + do + _exec_if_function "${BASHBOT_EVENT_INLINE[${key}]}" "inline" "${key}" "${debug}" + done +} +event_message() { + local key debug="$1" + # ${MESSAEG[*]} event_message + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_MESSAGE[@]}" + do + _exec_if_function "${BASHBOT_EVENT_MESSAGE[${key}]}" "message" "${key}" "${debug}" + done + + # ${TEXT[*]} event_text + if [ -n "${MESSAGE[0]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_TEXT[@]}" + do + _exec_if_function "${BASHBOT_EVENT_TEXT[${key}]}" "text" "${key}" "${debug}" + done + + # ${CMD[*]} event_cmd + if [ -n "${CMD[0]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_CMD[@]}" + do + _exec_if_function "${BASHBOT_EVENT_CMD[${key}]}" "command" "${key}" "${debug}" + done + fi + fi + # ${REPLYTO[*]} event_replyto + if [ -n "${REPLYTO[UID]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_REPLYTO[@]}" + do + _exec_if_function "${BASHBOT_EVENT_REPLYTO[${key}]}" "replyto" "${key}" "${debug}" + done + fi + + # ${FORWARD[*]} event_forward + if [ -n "${FORWARD[UID]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_FORWARD[@]}" + do + _exec_if_function && "${BASHBOT_EVENT_FORWARD[${key}]}" "forward" "${key}" "${debug}" + done + fi + + # ${CONTACT[*]} event_contact + if [ -n "${CONTACT[FIRST_NAME]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_CONTACT[@]}" + do + _exec_if_function "${BASHBOT_EVENT_CONTACT[${key}]}" "contact" "${key}" "${debug}" + done + fi + + # ${VENUE[*]} event_location + # ${LOCATION[*]} event_location + if [ -n "${LOCATION[LONGITUDE]}" ] || [ -n "${VENUE[TITLE]}" ]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_LOCATION[@]}" + do + _exec_if_function "${BASHBOT_EVENT_LOCATION[${key}]}" "location" "${key}" "${debug}" + done + fi + + # ${URLS[*]} event_file + # NOTE: compare again #URLS -1 blanks! + if [[ "${URLS[*]}" != " " ]]; then + # shellcheck disable=SC2153 + for key in "${!BASHBOT_EVENT_FILE[@]}" + do + _exec_if_function "${BASHBOT_EVENT_FILE[${key}]}" "file" "${key}" "${debug}" + done + fi + +} + diff --git a/modules/sendMessage.sh b/modules/sendMessage.sh index 07f5613..4185948 100644 --- a/modules/sendMessage.sh +++ b/modules/sendMessage.sh @@ -6,7 +6,7 @@ # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # # shellcheck disable=SC1117 -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # will be automatically sourced from bashbot @@ -137,15 +137,63 @@ remove_keyboard() { #JSON='"text":"$2", "reply_markup": {"remove_keyboard":true}' } +# buttons will specified as "texts +#|url" ... "text|url" empty arg starts new row +# url not starting with http:// or https:// will be send as callback_data +send_inline_buttons(){ + send_inline_keyboard "$1" "$2" "$(_button_row "${@:3}")" +} + +# $1 CHAT $2 message-id $3 buttons +# buttons will specified as "text|url" ... "text|url" empty arg starts new row +# url not starting with http:// or https:// will be send as callback_data +edit_inline_buttons(){ + edit_inline_keyboard "$1" "$2" "$(_button_row "${@:3}")" +} + + +# $1 CHAT $2 message $3 button text $4 button url +send_button() { + send_inline_keyboard "$1" "$2" '[{"text":"'"$(JsonEscape "$3")"'", "url":"'"$4"'"}]' +} + +# helper function to create json for a button row +# buttons will specified as "text|url" ... "text|url" empty arg starts new row +# url not starting with http:// or https:// will be send as callback_data +_button_row() { + [ -z "$1" ] && return 1 + local arg type json sep + for arg in "$@" + do + [ -z "${arg}" ] && sep="],[" && continue + type="callback_data" + [[ "${arg##*|}" =~ ^(https*://|tg://) ]] && type="url" + json+="${sep}"'{"text":"'"$(JsonEscape "${arg%|*}")"'", "'"${type}"'":"'"${arg##*|}"'"}' + sep="," + done + printf "[%s]" "${json}" +} + +# raw inline functions, for special use +# $1 CHAT $2 message-id $3 keyboard +edit_inline_keyboard() { + sendJson "$1" '"message_id":'"$2"', "reply_markup": {"inline_keyboard": [ '"$3"' ]}' "${URL}/editMessageReplyMarkup" + # JSON='"message_id":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}' +} + + # $1 CHAT $2 message $3 keyboard send_inline_keyboard() { - local text; text='"text":"'$(JsonEscape "$2")'"'; [ -z "$2" ] && text='"text":"'"Keyboard:"'"' + local text; text='"text":"'$(JsonEscape "$2")'"'; [ -z "$2" ] && text='"text":"..."' sendJson "$1" "${text}"', "reply_markup": {"inline_keyboard": [ '"$3"' ]}' "${MSG_URL}" # JSON='"text":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}' } -# $1 CHAT $2 message $3 button text $4 URL -send_button() { - send_inline_keyboard "$1" "$2" '[ {"text":"'"$(JsonEscape "$3")"'", "url":"'"$4"'"}]' + +# $1 callback id, $2 text to show, alert if not empty +answer_callback_query() { + local alert + [ -n "$3" ] && alert='","show_alert": true' + sendJson "" '"callback_query_id": "'"$1"'","text":"'"$2${alert}"'"' "${URL}/answerCallbackQuery" } # $1 chat, $2 file_id on telegram server @@ -160,7 +208,7 @@ if detect_curl ; then # $1 chat $3 ... $n URL or ID send_album(){ [ -z "$1" ] && return 1 - [ -z "$3" ] && return 2 # minimum 2 files + [ -z "$3" ] && return 2 # minimum 2 files local CHAT JSON IMAGE; CHAT="$1"; shift for IMAGE in "$@" do @@ -196,13 +244,13 @@ send_file(){ else # we have a file, check file location ... media="FILE" - [[ "${file}" = *'..'* || "${file}" = '.'* ]] && err=1 # no directory traversal + [[ "${file}" = *'..'* || "${file}" = '.'* ]] && err=1 # no directory traversal if [[ "${file}" = '/'* ]] ; then - [[ ! "${file}" =~ ${FILE_REGEX} ]] && err=2 # absolute must match REGEX + [[ ! "${file}" =~ ${FILE_REGEX} ]] && err=2 # absolute must match REGEX else - file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # others must be in UPLOADDIR + file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # others must be in UPLOADDIR fi - [ ! -r "${file}" ] && err=3 # and file must exits of course + [ ! -r "${file}" ] && err=3 # and file must exits of course # file path error, generate error response if [ -n "${err}" ]; then BOTSENT=(); BOTSENT[OK]="false" @@ -295,7 +343,7 @@ forward_message() { [ -z "$3" ] && return sendJson "$1" '"from_chat_id": '"$2"', "message_id": '"$3"'' "${URL}/forwardMessage" } -forward() { # backward compatibility +forward() { # backward compatibility forward_message "$@" || return } @@ -323,7 +371,7 @@ send_message() { sent=y fi if [ -n "${keyboard}" ]; then - if [[ "${keyboard}" != *"["* ]]; then # pre 0.60 style + if [[ "${keyboard}" != *"["* ]]; then # pre 0.60 style keyboard="[ ${keyboard//\" \"/\" \] , \[ \"} ]" fi send_keyboard "$1" "${text}" "${keyboard}" diff --git a/mycommands.conf b/mycommands.conf index e06fe09..aace68d 100644 --- a/mycommands.conf +++ b/mycommands.conf @@ -1,4 +1,5 @@ #!/bin/bash +# shellcheck disable=SC2034 ####################################################### # # File: mycommands.conf @@ -11,7 +12,7 @@ # Author: KayM (gnadelwartz), kay@rrr.de # Created: 09.01.2021 07:27 # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ####################################################### ########## @@ -46,6 +47,10 @@ Edit commands and messages in mycommands.sh! # To enable this option in your bot, send the /setinline command to @BotFather. export INLINE="0" +# Set CALLBACK to 1 in order to receive callback queries. +# callbacks are sent from inline_keyboards (buttons) attached tp bot messages +export CALLBACK="0" + # if your bot is group admin it get commands sent to other bots # Set MEONLY to 1 to ignore commands sent to other bots export MEONLY="0" @@ -63,7 +68,11 @@ unset BASHBOT_RETRY # set value for adaptive sleeping while waiting for uodates in millisconds # max slepp between polling updates 10s (default 5s) -export BASHBOT_SLEEP="10000" +# export BASHBOT_SLEEP="10000" + +# max slepp between polling updates 2s (default 5s) +# export BASHBOT_SLEEP="2000" + # add 0.2s if no update available, up to BASHBOT_SLEEP (default 0.1s) export BASHBOT_SLEEP_STEP="200" diff --git a/mycommands.sh b/mycommands.sh index bc27071..c8fcd2c 100644 --- a/mycommands.sh +++ b/mycommands.sh @@ -13,7 +13,7 @@ # License: WTFPLv2 http://www.wtfpl.net/txt/copying/ # Author: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ####################################################### # shellcheck disable=SC1117 @@ -131,6 +131,9 @@ else '/echo'*) # example echo command send_normal_message "${CHAT[ID]}" "${MESSAGE}" ;; + '/button'*)# inline button, set CALLBACK=1 for processing callbacks + send_inline_buttons "${CHAT[ID]}" "Press Button ..." " Button |RANDOM-BUTTON" + ;; '/question'*) # start interactive questions checkproc if [ "${res}" -gt 0 ] ; then @@ -179,6 +182,47 @@ else esac } + mycallbacks() { + ####################### + # callbacks from buttons attached to messages will be processed here + # no standard use case for processing callbacks, let's log them for some users and chats + case "${iBUTTON[USER_ID]}+${iBUTTON[CHAT_ID]}" in + 'USERID1+'*) # do something for all callbacks from USER + printf "%s: U=%s C=%s D=%s\n" "$(date)" "${iBUTTON[USER_ID]}" "${iBUTTON[CHAT_ID]}" "${iBUTTON[DATA]}"\ + >>"${DATADIR}/${iBUTTON[USER_ID]}.log" + answer_callback_query "${iBUTTON[ID]}" "Request has been logged in your user log..." + return + ;; + *'+CHATID1') # do something for all callbacks from CHAT + printf "%s: U=%s C=%s D=%s\n" "$(date)" "${iBUTTON[USER_ID]}" "${iBUTTON[CHAT_ID]}" "${iBUTTON[DATA]}"\ + >>"${DATADIR}/${iBUTTON[CHAT_ID]}.log" + answer_callback_query "${iBUTTON[ID]}" "Request has been logged in chat log..." + return + ;; + 'USERID2+CHATID2') # do something only for callbacks form USER in CHAT + printf "%s: U=%s C=%s D=%s\n" "$(date)" "${iBUTTON[USER_ID]}" "${iBUTTON[CHAT_ID]}" "${iBUTTON[DATA]}"\ + >>"${DATADIR}/${iBUTTON[USER_ID]}-${iBUTTON[CHAT_ID]}.log" + answer_callback_query "${iBUTTON[ID]}" "Request has been logged in user-chat log..." + return + ;; + *) # all other callbacks are processed here + local callback_answer + # your processing here ... + # message available? + if [[ -n "${iBUTTON[CHAT_ID]}" && -n "${iBUTTON[MESSAGE_ID]}" ]]; then + if [ "${iBUTTON[DATA]}" = "RANDOM-BUTTON" ]; then + callback_answer="Button pressed" + edit_inline_buttons "${iBUTTON[CHAT_ID]}" "${iBUTTON[MESSAGE_ID]}" "Button ${RANDOM}|RANDOM-BUTTON" + fi + else + callback_answer="Button to old, sorry." + fi + # Telegram needs an ack each callback query, default empty + answer_callback_query "${iBUTTON[ID]}" "${callback_answer}" + ;; + esac + } + myinlines() { ####################### # Inline query examples, do not use them in production (except image search ;-) diff --git a/mycommands.sh.clean b/mycommands.sh.clean index 9ae73e2..369d384 100644 --- a/mycommands.sh.clean +++ b/mycommands.sh.clean @@ -10,7 +10,7 @@ # License: WTFPLv2 http://www.wtfpl.net/txt/copying/ # Author: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ####################################################### # shellcheck disable=SC1117 @@ -55,6 +55,8 @@ else [ -n "${REMOVEKEYBOARD}" ] && remove_keyboard "${CHAT[ID]}" & [[ -n "${REMOVEKEYBOARD_PRIVATE}" && "${CHAT[ID]}" == "${USER[ID]}" ]] && remove_keyboard "${CHAT[ID]}" & + # uncommet to fix first letter upper case because of smartphone auto correction + #[[ "${MESSAGE}" =~ ^/[[:upper:]] ]] && MESSAGE="${MESSAGE:0:1}$(tr '[:upper:]' '[:lower:]' <<<"${MESSAGE:1:1}")${MESSAGE:2}" case "${MESSAGE}" in ################## # example command, replace them by your own @@ -77,6 +79,19 @@ else esac } + mycallbacks() { + ####################### + # callbacks from buttons attached to messages will be processed here + case "${iBUTTON[USER_ID]}+${iBUTTON[CHAT_ID]}" in + *) # all other callbacks are processed here + local callback_answer + : # your processing here ... + : + # Telegram needs an ack each callback query, default empty + answer_callback_query "${iBUTTON[ID]}" "${callback_answer}" + ;; + esac + } myinlines() { ####################### # this fuinction is called only if you has set INLINE=1 !! diff --git a/scripts/interactive.sh.clean b/scripts/interactive.sh.clean index bbb7434..15ec448 100755 --- a/scripts/interactive.sh.clean +++ b/scripts/interactive.sh.clean @@ -12,7 +12,7 @@ # This file is public domain in the USA and all free countries. # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 ######################################################################## ###### diff --git a/test/ADD-test-new.sh b/test/ADD-test-new.sh index 59b933b..9df5b48 100755 --- a/test/ADD-test-new.sh +++ b/test/ADD-test-new.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # magic to ensure that we're always inside the root of our application, diff --git a/test/ALL-tests.inc.sh b/test/ALL-tests.inc.sh index 3f519fe..66ac9a6 100644 --- a/test/ALL-tests.inc.sh +++ b/test/ALL-tests.inc.sh @@ -11,7 +11,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # common variables diff --git a/test/a-commit-test.sh b/test/a-commit-test.sh index dfb9ac9..bc41832 100755 --- a/test/a-commit-test.sh +++ b/test/a-commit-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== ../dev/hooks/pre-commit.sh diff --git a/test/b-example-test.sh b/test/b-example-test.sh index f60d0a6..d26411b 100644 --- a/test/b-example-test.sh +++ b/test/b-example-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions diff --git a/test/c-init-test.sh b/test/c-init-test.sh index aca8c1b..8a0564d 100755 --- a/test/c-init-test.sh +++ b/test/c-init-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions diff --git a/test/d-JSON.sh-test.sh b/test/d-JSON.sh-test.sh index bb996b7..08a1f42 100755 --- a/test/d-JSON.sh-test.sh +++ b/test/d-JSON.sh-test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 # include common functions and definitions # shellcheck source=test/ALL-tests.inc.sh diff --git a/test/d-process_inline-test.sh b/test/d-process_inline-test.sh index c71808e..601832a 100755 --- a/test/d-process_inline-test.sh +++ b/test/d-process_inline-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions @@ -41,7 +41,7 @@ source <( printf 'UPD=( %s )' "$(sed <<<"${UPDATE}" -E -e 's/\t/=/g' -e 's/=(tru printf "Check process_inline ...\n" printf " ... with JsonDecode Bash\n" set -x -{ process_inline "0"; set +x; } >>"${LOGFILE}" 2>&1; +{ process_inline_query "0"; set +x; } >>"${LOGFILE}" 2>&1; # output processed input print_array "iQUERY" >"${OUTPUTFILE}" diff --git a/test/d-process_message-test.sh b/test/d-process_message-test.sh index 502d4ad..dd38dd8 100755 --- a/test/d-process_message-test.sh +++ b/test/d-process_message-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions diff --git a/test/d-send_message-test.sh b/test/d-send_message-test.sh index aadf8c9..e55b96b 100755 --- a/test/d-send_message-test.sh +++ b/test/d-send_message-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions diff --git a/test/d-send_message-test/d-send_message-test.result b/test/d-send_message-test/d-send_message-test.result index 3399681..e0f1017 100644 --- a/test/d-send_message-test/d-send_message-test.result +++ b/test/d-send_message-test/d-send_message-test.result @@ -34,10 +34,10 @@ URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?12345 chat:123456 JSON:"text":"\# test for new inline button" URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?123456789:BASHBOTTESTSCRIPTbashbottestscript_/sendMessage -chat:123456 JSON:"text":"Text plus keyboard will appear in chat", "reply_markup": {"inline_keyboard": [ [ {"text":"Button Text", "url":"https://www..."}] ]} +chat:123456 JSON:"text":"Text plus keyboard will appear in chat", "reply_markup": {"inline_keyboard": [ [{"text":"Button Text", "url":"https://www..."}] ]} URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?123456789:BASHBOTTESTSCRIPTbashbottestscript_/sendMessage -chat:123456 JSON:"text":"STABILO 88\/240 Fineliner point 88\n\n[https:\/\/images\-na\.ssl\-images\-amazon\.com\/images\/I\/41oypA3kmHL\.l_SX300\.jpg]\nsecond part of text\nplus newline\.", "reply_markup": {"inline_keyboard": [ [ {"text":"Bei Amazon ansehen \.\.\.", "url":"https://www.amazon.de/dp/B014TN3JYW"}] ]} +chat:123456 JSON:"text":"STABILO 88\/240 Fineliner point 88\n\n[https:\/\/images\-na\.ssl\-images\-amazon\.com\/images\/I\/41oypA3kmHL\.l_SX300\.jpg]\nsecond part of text\nplus newline\.", "reply_markup": {"inline_keyboard": [ [{"text":"Bei Amazon ansehen \.\.\.", "url":"https://www.amazon.de/dp/B014TN3JYW"}] ]} URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?123456789:BASHBOTTESTSCRIPTbashbottestscript_/sendMessage chat:123456 JSON:"text":"\# test for sendfile" diff --git a/test/d-user_is-test.sh b/test/d-user_is-test.sh index 9d2802e..8daf09b 100755 --- a/test/d-user_is-test.sh +++ b/test/d-user_is-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions diff --git a/test/e-env-test.sh b/test/e-env-test.sh index f9e80dc..85dd792 100755 --- a/test/e-env-test.sh +++ b/test/e-env-test.sh @@ -10,7 +10,7 @@ # LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/ # AUTHOR: KayM (gnadelwartz), kay@rrr.de # -#### $$VERSION$$ v1.30-0-g3266427 +#### $$VERSION$$ v1.40-0-gf9dab50 #=============================================================================== # include common functions and definitions