Merge pull request #173 Version 1.40

Prepare Version 1.40
This commit is contained in:
Kay Marquardt 2021-02-04 18:50:39 +01:00 committed by GitHub
commit d15b4f5070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 2135 additions and 965 deletions

View File

@ -223,8 +223,8 @@ Written by Drew (@topkecleon) and Kay M (@gnadelwartz).
<p>Released to the public domain wherever applicable. Elsewhere, consider it released under the <a href="http://www.wtfpl.net/txt/copying/">WTFPLv2</a>.</p>
<p>Linted by <a href="https://github.com/koalaman/shellcheck">#ShellCheck</a></p>
<h2>Prerequisites</h2>
<p>Uses <a href="http://github.com/dominictarr/JSON.sh">JSON.sh</a> and the magic of sed.</p>
<p>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 <a href="https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands">coreutils</a>, <a href="https://en.wikipedia.org/wiki/BusyBox#Commands">busybox</a> or <a href="https://landley.net/toybox/help.html">toybox</a>, see <a href="doc/7_develop.md#common-commands">Developer Notes</a>.</p>
<p>Uses <a href="http://github.com/dominictarr/JSON.sh">JSON.sh</a>/<a href="https://github.com/step-/JSON.awk">JSON.awk</a> and the magic of sed.</p>
<p>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 <a href="https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands">coreutils</a>, <a href="https://en.wikipedia.org/wiki/BusyBox#Commands">busybox</a> or <a href="https://landley.net/toybox/help.html">toybox</a>, see <a href="doc/7_develop.md#common-commands">Developer Notes</a>.</p>
<p><strong>Note for MacOS and BSD Users:</strong> Bashbot will not run without installing additional software as it uses modern bash and (gnu) grep/sed features. See <a href="doc/0_install.md">Install Bashbot</a>.</p>
<p><strong>Note for embedded systems:</strong> You need to install a "real" bash as the vanilla installation of busybox or toybox is not sufficient. See <a href="doc/0_install.md">Install Bashbot</a>.</p>
<p>Bashbot <a href="https://github.com/topkecleon/telegram-bot-bash">Documentation</a> and <a href="https://github.com/topkecleon/telegram-bot-bash/releases">Downloads</a> are available on <a href="https://www.github.com">www.github.com</a>.</p>
@ -284,7 +284,8 @@ Written by Drew (@topkecleon) and Kay M (@gnadelwartz).
<li>Setup your environment</li>
<li>Bashbot test suite</li>
</ul></li>
<li><a href="examples/README.md">Examples Directory</a></li>
<li><a href="examples">Examples Directory</a></li>
<li><a href="examples/webhook">Webhook Example</a></li>
</ul>
<h3>Your very first bashbot in a nutshell</h3>
<p>To install and run bashbot you need access to a Linux/Unix command line with bash, a <a href="https://telegram.org">Telegram client</a> and a mobile phone <a href="https://telegramguide.com/create-a-telegram-account/">with a Telegram account</a>.</p>
@ -340,7 +341,7 @@ It features background tasks and interactive chats, and can serve as an interfac
<p>Running a Telegram Bot means it is connected to the public and you never know what's send to your Bot.</p>
<p>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 <a href="https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells">Implications of wrong quoting</a>.</p>
<p>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 <code>set -f</code> and quote everything). In addition remove unused scripts and examples from your Bot (e.g. everything in <code>example/</code>) and disable/remove all unused bot commands.</p>
<p>It's important to escape or remove <code>$</code> in input from user, files or network (<em>as bashbot does</em>). One of the powerful features of Unix shells is variable and command substitution using <code>${}</code> and<code>$()</code> can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped <code>$</code> is included in untrusted input (e.g. <code>$$</code> or <code>$(rm -rf /*)</code>).</p>
<p>It's important to escape or remove <code>$</code> in input from user, files or network (<em>as bashbot does</em>). One of the powerful features of Unix shells is variable and command substitution using <code>${}</code> and <code>$()</code> can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped <code>$</code> is included in untrusted input (e.g. <code>$$</code> or <code>$(rm -rf /*)</code>).</p>
<p>A powerful tool to improve your scripts is <code>shellcheck</code>. You can <a href="https://www.shellcheck.net/">use it online</a> or <a href="https://github.com/koalaman/shellcheck#installing">install shellcheck locally</a>. 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 <a href="doc/7_develop.md">test suite</a> to check if important functionality is working as expected.</p>
<h3>Use printf whenever possible</h3>
<p>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. <a href="https://unix.stackexchange.com/a/6581">Use printf whenever possible</a>.</p>
@ -349,8 +350,9 @@ It features background tasks and interactive chats, and can serve as an interfac
<p><strong>Never run your Bot as root, this is the most dangerous you can do!</strong> Usually the user 'nobody' has almost no rights on Linux/Unix systems. See <a href="doc/4_expert.md">Expert use</a> on how to run your Bot as an other user.</p>
<h3>Secure your Bot installation</h3>
<p><strong>Your Bot configuration must not be readable by other users.</strong> Everyone who can read your Bots token is able to act as your Bot and has access to all chats the Bot is in!</p>
<p>Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config in<code>config.jssh</code> 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<code>count.jssh</code> and <code>data-bot-bash</code> only, all other files must be write protected.</p>
<p>To set access rights for your bashbot installation to a reasonable default run<code>sudo ./bashbot.sh init</code> after every update or change to your installation directory.</p>
<p>Everyone with read access to your Bot files can extract your Bots data. Especially your Bot config in <code>config.jssh</code> 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 <code>count.jssh</code>, <code>data-bot-bash/</code> and <code>logs/</code> only, all other files must be write protected.</p>
<p>To set access rights for your bashbot installation to a reasonable default run <code>sudo ./bashbot.sh init</code> after every update or change to your installation directory.</p>
<p><em>Note</em>: Keep old log files in a safe place or even better delete them, they are GDPR relevant and <a href="https://github.com/topkecleon/telegram-bot-bash/issues/174">may contain information</a> you don't want to be public.</p>
<h2>FAQ</h2>
<h3>Is this Bot insecure?</h3>
<p>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 ...</p>
@ -364,8 +366,8 @@ It features background tasks and interactive chats, and can serve as an interfac
<li>no database, not event driven, not object oriented ...</li>
</ul>
<h3>Can I have the single bashbot.sh file back?</h3>
<p>At the beginning bashbot was simply the file<code>bashbot.sh</code> that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.</p>
<p>Hey no problem, if you are finished with your cool bot, run<code>dev/make-standalone.sh</code> to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see <a href="doc/7_develop.md">Create a stripped down version of your Bot</a>.</p>
<p>At the beginning bashbot was simply the file <code>bashbot.sh</code> that you could copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.</p>
<p>Hey no problem, if you are finished with your cool bot, run <code>dev/make-standalone.sh</code> to create a stripped down version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see <a href="doc/7_develop.md">Create a stripped down version of your Bot</a>.</p>
<h3>Can I send messages from CLI and scripts?</h3>
<p>Of course you can send messages from command line and scripts! Simply install bashbot as <a href="#Your-really-first-bashbot-in-a-nutshell">described here</a>, send the message '/start' to set yourself as botadmin and then stop the bot with <code>./bashbot.sh stop</code>.</p>
<p>Bashbot provides some ready to use scripts for sending messages from command line in <code>bin/</code> dir, e.g. <code>send_message.sh</code>.</p>
@ -390,6 +392,6 @@ It features background tasks and interactive chats, and can serve as an interfac
<p>@Gnadelwartz</p>
<h2>That's it all guys!</h2>
<p>If you feel that there's something missing or if you found a bug, feel free to submit a pull request!</p>
<h4>$$VERSION$$ v1.30-0-g3266427</h4>
<h4>$$VERSION$$ v1.40-dev-34-g1440d56</h4>
</body>
</html>

View File

@ -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

View File

@ -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

View File

@ -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:
#

View File

@ -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.
#

View File

@ -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

View File

@ -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 2>/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 <CTRL+C> to stop or <Enter> 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

85
bin/any_command.sh Executable file
View File

@ -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

View File

@ -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
}

143
bin/bashbot_init.inc.sh Normal file
View File

@ -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 <CTRL+C> to stop or <Enter> 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}<C>lean file, <E>xamples or <N>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}<I>nline queries, <C>allback buttons, <B>oth or <N>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
}

View File

@ -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 -----

View File

@ -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

54
bin/edit_buttons.sh Executable file
View File

@ -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

67
bin/edit_message.sh Executable file
View File

@ -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

47
bin/kickban_user.sh Executable file
View File

@ -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

42
bin/process_update.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
# shellcheck disable=SC1090,SC2034,SC2059
#===============================================================================
#
# FILE: bin/process_update.sh
#
USAGE='process_update.sh [-h|--help] [debug] [<file]'
#
# DESCRIPTION: processes ONE telegram update read from stdin, e.g. form file or webhook
#
# -h - display short help
# --help - this help
#
# LICENSE: WTFPLv2 http://www.wtfpl.net/txt/copying/
# AUTHOR: KayM (gnadelwartz), kay@rrr.de
# CREATED: 30.01.2021 19:14
#
#### $$VERSION$$ v1.40-0-gf9dab50
#===============================================================================
####
# parse args
COMMAND="process_update"
# set bashbot environment
source "${0%/*}/bashbot_env.inc.sh" "debug" # debug
print_help "${1:-nix}"
####
# ready, do stuff here -----
# read json from stdin and convert update format
json='{"result": ['"$(cat)"']}'
UPDATE="$(${JSONSHFILE} -b -n <<<"${json}" 2>/dev/null)"
# assign to bashbot ARRAY
Json2Array 'UPD' <<<"${UPDATE}"
# process telegram update
"${COMMAND}" "0" "$1"

46
bin/promote_user.sh Executable file
View File

@ -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

View File

@ -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}}"

54
bin/send_buttons.sh Executable file
View File

@ -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

View File

@ -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

1
bin/send_edit_message.sh Symbolic link
View File

@ -0,0 +1 @@
edit_message.sh

View File

@ -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

View File

@ -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

View File

@ -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]}"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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!

View File

@ -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!

View File

@ -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!

View File

@ -7,7 +7,7 @@
#
# Usage: source inject-json.sh
#
#### $$VERSION$$ v1.30-0-g3266427
#### $$VERSION$$ v1.40-0-gf9dab50
##############################################################
# download JSON.sh

View File

@ -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

View File

@ -7,7 +7,7 @@
#
# Options: --notest - skip tests
#
#### $$VERSION$$ v1.30-0-g3266427
#### $$VERSION$$ v1.40-0-gf9dab50
##############################################################
#shellcheck disable=SC1090

View File

@ -1,7 +1,9 @@
data-bot-bash/*
webhook-fifo
JSON.awk
bashbot.rc
mycommands.sh
mycommands.conf
awk-patch.sh
*.jssh*
botacl

View File

@ -7,7 +7,7 @@
#
# Usage: source make-hmtl
#
#### $$VERSION$$ v1.30-0-g3266427
#### $$VERSION$$ v1.40-0-gf9dab50
##############################################################
# check for correct dir

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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 href="([^>]+)">([^<#]+)<\/a>/\2 [\1]/' <README.html |\
sed -E 's/<a href="([^>]+)">([^<#]+)<\/a>/\2 [\1]/g' <README.html |\
html2text -style pretty -width 90 - >>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}"

View File

@ -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

View File

@ -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

View File

@ -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 <b>bold</b>"
```
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

View File

@ -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 bots 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

View File

@ -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

View File

@ -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

View File

@ -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 <b>html</b> 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 <b>html</b> 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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
########################################################################
######

View File

@ -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

View File

@ -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
########################################################################
######

View File

@ -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
########################################################################
######

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/<your_bot_token>`
- 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://<yourservername>/telegram/<your_bot_token>/` will execute
`index.php` and write received JSON to file `data-bot-bash/webhook-fifo`.
E.g. the URL `https://<yourservername>/telegram/<your_bot_token>/?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/<your_bot_token>/` 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

View File

@ -0,0 +1,96 @@
<?php
/************************************************************
* @file examples/webhook/index.php
* @description example webhook implementation for apache
* write to fifo/file if writeable, else pipe to command
*
* first line of BASHBOT_HOME is used as bot home (optional)
* must start with /, not contain /. and min 20 characters long
*
* @author KayM (gnadelwartz), kay@rrr.de
* @license http://www.wtfpl.net/txt/copying/ WTFPLv2
* @since 30.01.2021 20:24
*
#### $$VERSION$$ v1.40-0-gf9dab50
***********************************************************/
// bashbot home dir
$CONFIG_HOME='BASHBOT_HOME';
$BASHBOT_HOME='/usr/local/telegram-bot-bash';
// read from config file
if (file_exists($CONFIG_HOME)) {
$tmp = trim(fgets(fopen($CONFIG_HOME, 'r')));
// start with '/', not '/.', min 20 chars
if (substr($tmp,0,1) == '/' && strlen($tmp) >= 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);
}
?>

View File

@ -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"}]}}

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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() {

View File

@ -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}"
}

496
modules/processUpdates.sh Normal file
View File

@ -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
}

View File

@ -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}"

View File

@ -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"

View File

@ -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 ;-)

View File

@ -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 !!

View File

@ -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
########################################################################
######

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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