Merge pull request #118 from topkecleon/develop

0.94 pre release for wider testing
This commit is contained in:
Kay Marquardt 2020-05-15 18:14:46 +02:00 committed by GitHub
commit e721a1aaa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 769 additions and 340 deletions

View File

@ -223,6 +223,6 @@ It features background tasks and interactive chats, and can serve as an interfac
<p>@Gnadelwartz</p>
<h2>That's it!</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$$ v0.94-dev2-0-g3d636f7</h4>
<h4>$$VERSION$$ v0.94-pre-0-gac2ec02</h4>
</body>
</html>

View File

@ -180,4 +180,4 @@ This may happen if to many wrong requests are sent to api.telegram.org, e.g. usi
If you feel that there's something missing or if you found a bug, feel free to submit a pull request!
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -253,4 +253,4 @@ tor proxy on your server you may uncomment the ```BASHBOT_CURL_ARGS``` line in
If you feel that there's something missing or if you found a bug, feel free to
submit a pull request!
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-4-gd28f975
# used events:
#
@ -99,7 +99,7 @@ if [[ "$1" = "start"* ]]; then
antiFlood_multievent(){
# not started
[ "${ANTIFL_CHATS["${CHAT[ID]}","level"]}" = "" ] && return
[ -z "${ANTIFL_CHATS["${CHAT[ID]}","level"]}" ] && return
# count user flood text
if [ "$1" = "text" ]; then
if [ "${#MESSAGE[0]}" -gt "${ANTIFL_CHATS["${CHAT[ID]}","level"]}" ]; then

View File

@ -4,7 +4,7 @@
# Addons can register to bashbot events at statup
# by providing their name and a callback per event
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
#
# If an event occours each registered event function is called.
#

View File

@ -1,7 +1,7 @@
#!/bin/sh
# description: Start or stop telegram-bash-bot
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-11-gac9ca60
# shellcheck disable=SC2009
# shellcheck disable=SC2181
@ -76,6 +76,11 @@ case "$1" in
'suspendback'|'resumeback'|'killback')
$runcmd "$start $1"
RETVAL=$?
# kill inotifywait from runuser if long running bg scripts use it
KILLINOTIFY=""
if [ "$1" != "resumeback" ] && [ -n "${KILLINOTIFY}" ]; then
kill -9 "$(ps -u "$runas" | grep inotifywait | sed 's/ .*//')" >/dev/null 2>&1
fi
;;
*)
echo "Usage: $0 { start | stop | restart | reload | restartback | suspendback | resumeback | killback }"

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-4-gd28f975
#
# Exit Codes:
# - 0 sucess (hopefully)
@ -23,7 +23,7 @@
# shellcheck disable=SC2140
# are we runnig in a terminal?
if [ -t 1 ] && [ "$TERM" != "" ]; then
if [ -t 1 ] && [ -n "$TERM" ]; then
CLEAR='clear'
RED='\e[31m'
GREEN='\e[32m'
@ -31,6 +31,23 @@ if [ -t 1 ] && [ "$TERM" != "" ]; then
NC='\e[0m'
fi
# some important helper functions
# returns true if command exist
_exists()
{
[ "$(LC_ALL=C type -t "$1")" = "file" ]
}
# execute function if exists
_exec_if_function() {
[ "$(LC_ALL=C type -t "${1}")" != "function" ] || "$@"
}
# returns true if function exist
_is_function()
{
[ "$(LC_ALL=C type -t "$1")" = "function" ]
}
# get location and name of bashbot.sh
SCRIPT="$0"
REALME="${BASH_SOURCE[0]}"
@ -39,7 +56,7 @@ RUNDIR="$(dirname "$0")"
MODULEDIR="${SCRIPTDIR}/modules"
# adjust locations based on source and real name
if [ "${SCRIPT}" != "${REALME}" ] || [ "$1" = "source" ]; then
SOURCE="yes"
else
@ -47,17 +64,17 @@ else
MODULEDIR="./$(basename "${MODULEDIR}")"
fi
if [ "$BASHBOT_HOME" != "" ]; then
if [ -n "$BASHBOT_HOME" ]; then
SCRIPTDIR="$BASHBOT_HOME"
[ "${BASHBOT_ETC}" = "" ] && BASHBOT_ETC="$BASHBOT_HOME"
[ "${BASHBOT_VAR}" = "" ] && BASHBOT_VAR="$BASHBOT_HOME"
[ -z "${BASHBOT_ETC}" ] && BASHBOT_ETC="$BASHBOT_HOME"
[ -z "${BASHBOT_VAR}" ] && BASHBOT_VAR="$BASHBOT_HOME"
fi
ADDONDIR="${BASHBOT_ETC:-./addons}"
RUNUSER="${USER}" # USER is overwritten by bashbot array
if [ "${SOURCE}" != "yes" ] && [ "$BASHBOT_HOME" = "" ] && ! cd "${RUNDIR}" ; then
if [ "${SOURCE}" != "yes" ] && [ -z "$BASHBOT_HOME" ] && ! cd "${RUNDIR}" ; then
echo -e "${RED}ERROR: Can't change to ${RUNDIR} ...${NC}"
exit 1
else
@ -69,62 +86,71 @@ if [ ! -w "." ]; then
ls -ld .
fi
#####################
# Setup and check environment if BOTTOKEN is NOT set
TOKENFILE="${BASHBOT_ETC:-.}/token"
if [ ! -f "${TOKENFILE}" ]; then
if [ "${CLEAR}" = "" ] && [ "$1" != "init" ]; then
echo "Running headless, run ${SCRIPT} init first!"
BOTADMIN="${BASHBOT_ETC:-.}/botadmin"
BOTACL="${BASHBOT_ETC:-.}/botacl"
DATADIR="${BASHBOT_VAR:-.}/data-bot-bash"
# !!!!! DEPRECATED !!!!!
COUNTFILE="${BASHBOT_VAR:-.}/count"
# we assume everthing is already set up correctly if we have TOKEN
if [ -z "${BOTTOKEN}" ]; then
# BOTTOKEN empty read from file
if [ ! -f "${TOKENFILE}" ]; then
if [ -z "${CLEAR}" ] && [ "$1" != "init" ]; then
echo "Running headless, set BOTTOKEN or run ${SCRIPT} init first!"
exit 2
else
else
${CLEAR}
echo -e "${RED}TOKEN MISSING.${NC}"
echo -e "${ORANGE}PLEASE WRITE YOUR TOKEN HERE OR PRESS CTRL+C TO ABORT${NC}"
read -r token
printf '%s\n' "${token}" > "${TOKENFILE}"
fi
fi
BOTADMIN="${BASHBOT_ETC:-.}/botadmin"
if [ ! -f "${BOTADMIN}" ]; then
if [ "${CLEAR}" = "" ]; then
read -r BOTTOKEN
printf '%s\n' "${BOTTOKEN}" > "${TOKENFILE}"
fi
fi
[ -z "${BOTTOKEN}" ] && BOTTOKEN="$(< "${TOKENFILE}")"
# setup botadmin file
if [ ! -f "${BOTADMIN}" ]; then
if [ -z "${CLEAR}" ]; then
echo "Running headless, set botadmin to AUTO MODE!"
printf '%s\n' '?' > "${BOTADMIN}"
else
else
${CLEAR}
echo -e "${RED}BOTADMIN MISSING.${NC}"
echo -e "${ORANGE}PLEASE WRITE YOUR TELEGRAM ID HERE OR ENTER '?'${NC}"
echo -e "${ORANGE}TO MAKE FIRST USER TYPING '/start' TO BOTADMIN${NC}"
read -r admin
[ "${admin}" = "" ] && admin='?'
[ -z "${admin}" ] && admin='?'
printf '%s\n' "${admin}" > "${BOTADMIN}"
fi
fi
BOTACL="${BASHBOT_ETC:-.}/botacl"
if [ ! -f "${BOTACL}" ]; then
fi
fi
# setup botacl file
if [ ! -f "${BOTACL}" ]; then
echo -e "${ORANGE}Create empty ${BOTACL} file.${NC}"
printf '\n' >"${BOTACL}"
fi
DATADIR="${BASHBOT_VAR:-.}/data-bot-bash"
if [ ! -d "${DATADIR}" ]; then
fi
# setup data dir file
if [ ! -d "${DATADIR}" ]; then
mkdir "${DATADIR}"
elif [ ! -w "${DATADIR}" ]; then
elif [ ! -w "${DATADIR}" ]; then
echo -e "${RED}ERROR: Can't write to ${DATADIR}!.${NC}"
ls -ld "${DATADIR}"
exit 2
fi
COUNTFILE="${BASHBOT_VAR:-.}/count"
if [ ! -f "${COUNTFILE}" ]; then
fi
# setup count file !!!!! DEPRECATED !!!!!
if [ ! -f "${COUNTFILE}" ]; then
printf '\n' >"${COUNTFILE}"
elif [ ! -w "${COUNTFILE}" ]; then
elif [ ! -w "${COUNTFILE}" ]; then
echo -e "${RED}ERROR: Can't write to ${COUNTFILE}!.${NC}"
ls -l "${COUNTFILE}"
exit 2
fi
fi
BOTTOKEN="$(< "${TOKENFILE}")"
##################
# here we start with the real stuff
URL="${BASHBOT_URL:-https://api.telegram.org/bot}${BOTTOKEN}"
ME_URL=$URL'/getMe'
@ -136,10 +162,12 @@ declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR TOKENFILE BOTADMIN BOTACL
declare -rx BOTTOKEN URL ME_URL UPD_URL GETFILE_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 SERVICE NEWMEMBER
export res CAPTION
#################EW#
# read commamds file if we are not sourced
COMMANDS="${BASHBOT_ETC:-.}/commands.sh"
if [ "${SOURCE}" != "yes" ]; then
if [ ! -f "${COMMANDS}" ] || [ ! -r "${COMMANDS}" ]; then
@ -155,7 +183,7 @@ fi
# load modules
for modules in "${MODULEDIR:-.}"/*.sh ; do
# shellcheck source=./modules/aliases.sh
[ -r "${modules}" ] && source "${modules}" "source"
if ! _is_function "$(basename "${modules}")" && [ -r "${modules}" ]; then source "${modules}" "source"; fi
done
#################
@ -186,40 +214,24 @@ proclist() {
# $1 sting to search for proramm to kill
killallproc() {
local procid; procid="$(proclist "$1")"
if [ "${procid}" != "" ] ; then
if [ -n "${procid}" ] ; then
# shellcheck disable=SC2046
kill $(proclist "$1")
sleep 1
procid="$(proclist "$1")"
# shellcheck disable=SC2046
[ "${procid}" != "" ] && kill $(proclist -9 "$1")
[ -n "${procid}" ] && kill $(proclist -9 "$1")
fi
}
# returns true if command exist
_exists()
{
[ "$(LC_ALL=C type -t "$1")" = "file" ]
}
# execute function if exists
_exec_if_function() {
[ "$(LC_ALL=C type -t "${1}")" != "function" ] || "$@"
}
# returns true if function exist
_is_function()
{
[ "$(LC_ALL=C type -t "$1")" = "function" ]
}
declare -xr DELETE_URL=$URL'/deleteMessage'
delete_message() {
sendJson "${1}" '"message_id": '"${2}"'' "${DELETE_URL}"
}
get_file() {
[ "$1" = "" ] && return
[ -z "$1" ] && return
sendJson "" '"file_id": "'"${1}"'"' "${GETFILE_URL}"
printf '%s\n' "${URL}"/"$(JsonGetString <<< "${res}" '"result","file_path"')"
}
@ -228,7 +240,7 @@ get_file() {
TIMEOUT="${BASHBOT_TIMEOUT}"
[[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT="20"
if [ "${BASHBOT_WGET}" = "" ] && _exists curl ; then
if [ -z "${BASHBOT_WGET}" ] && _exists curl ; then
# simple curl or wget call, output to stdout
getJson(){
# shellcheck disable=SC2086
@ -237,18 +249,18 @@ if [ "${BASHBOT_WGET}" = "" ] && _exists curl ; then
# usage: sendJson "chat" "JSON" "URL"
sendJson(){
local chat="";
[ "${1}" != "" ] && chat='"chat_id":'"${1}"','
[ -n "${1}" ] && chat='"chat_id":'"${1}"','
# shellcheck disable=SC2086
res="$(curl -s ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" -d '{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' -X POST "${3}" \
res="$(curl -s -k ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" -d '{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' -X POST "${3}" \
-H "Content-Type: application/json" | "${JSONSHFILE}" -s -b -n )"
BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")"
BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "$res")"
[ "${SOURCE}" != "yes" ] && [ "${BASHBOT_EVENT_SEND[*]}" != "" ] && event_send "send" "$@" &
[ "${SOURCE}" != "yes" ] && [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "send" "$@" &
}
#$1 Chat, $2 what , $3 file, $4 URL, $5 caption
sendUpload() {
[ "$#" -lt 4 ] && return
if [ "$5" != "" ]; then
if [ -n "$5" ]; then
# shellcheck disable=SC2086
res="$(curl -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1" -F "$2=@$3;${3##*/}" -F "caption=$5" | "${JSONSHFILE}" -s -b -n )"
else
@ -256,7 +268,7 @@ if [ "${BASHBOT_WGET}" = "" ] && _exists curl ; then
res="$(curl -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1" -F "$2=@$3;${3##*/}" | "${JSONSHFILE}" -s -b -n )"
fi
BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")"
[ "${SOURCE}" != "yes" ] && [ "${BASHBOT_EVENT_SEND[*]}" != "" ] && event_send "upload" "$@" &
[ "${SOURCE}" != "yes" ] && [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "upload" "$@" &
}
else
# simple curl or wget call outputs result to stdout
@ -267,18 +279,18 @@ else
# usage: sendJson "chat" "JSON" "URL"
sendJson(){
local chat="";
[ "${1}" != "" ] && chat='"chat_id":'"${1}"','
[ -n "${1}" ] && chat='"chat_id":'"${1}"','
# shellcheck disable=SC2086
res="$(wget -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - --post-data='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' \
res="$(wget --no-check-certificate -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - --post-data='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' \
--header='Content-Type:application/json' "${3}" | "${JSONSHFILE}" -s -b -n )"
BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")"
BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "$res")"
[ "${SOURCE}" != "yes" ] && [ "${BASHBOT_EVENT_SEND[*]}" != "" ] && event_send "send" "$@" &
[ "${SOURCE}" != "yes" ] && [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "send" "$@" &
}
sendUpload() {
sendJson "$1" '"text":"Sorry, wget does not support file upload"' "${MSG_URL}"
BOTSENT[OK]="false"
[ "${SOURCE}" != "yes" ] && [ "${BASHBOT_EVENT_SEND[*]}" != "" ] && event_send "upload" "$@" &
[ "${SOURCE}" != "yes" ] && [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "upload" "$@" &
}
fi
@ -293,11 +305,11 @@ JsonEscape() {
# title caption description markup inlinekeyboard
title2Json(){
local title caption desc markup keyboard
[ "$1" != "" ] && title=',"title":"'$(JsonEscape "$1")'"'
[ "$2" != "" ] && caption=',"caption":"'$(JsonEscape "$2")'"'
[ "$3" != "" ] && desc=',"description":"'$(JsonEscape "$3")'"'
[ "$4" != "" ] && markup=',"parse_mode":"'$(JsonEscape "$4")'"'
[ "$5" != "" ] && keyboard=',"reply_markup":"'$(JsonEscape "$5")'"'
[ -n "$1" ] && title=',"title":"'$(JsonEscape "$1")'"'
[ -n "$2" ] && caption=',"caption":"'$(JsonEscape "$2")'"'
[ -n "$3" ] && desc=',"description":"'$(JsonEscape "$3")'"'
[ -n "$4" ] && markup=',"parse_mode":"'$(JsonEscape "$4")'"'
[ -n "$5" ] && keyboard=',"reply_markup":"'$(JsonEscape "$5")'"'
echo "${title}${caption}${desc}${markup}${keyboard}"
}
@ -332,7 +344,7 @@ JsonGetValue() {
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling
Json2Array() {
# shellcheck source=./commands.sh
[ "$1" = "" ] || source <( printf "$1"'=( %s )' "$(sed -E -n -e '/\["[-0-9a-zA-Z_,."]+"\]\t/ s/\t/=/gp' -e 's/=(true|false)/="\1"/')" )
[ -z "$1" ] || source <( printf "$1"'=( %s )' "$(sed -E -n -e '/\["[-0-9a-zA-Z_,."]+"\]\t/ s/\t/=/gp' -e 's/=(true|false)/="\1"/')" )
}
# output ARRAY as JSON.sh style data
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling
@ -360,7 +372,7 @@ process_client() {
CMD=( ); iQUERY=( )
iQUERY[ID]="${UPD["result",${num},"inline_query","id"]}"
[[ "${debug}" = *"debug"* ]] && cat <<< "$UPDATE" >>"MESSAGE.log"
if [ "${iQUERY[ID]}" = "" ]; then
if [ -z "${iQUERY[ID]}" ]; then
process_message "${num}" "${debug}"
else
process_inline "${num}" "${debug}"
@ -372,13 +384,14 @@ process_client() {
source "${COMMANDS}" "${debug}" &
# then all registered addons
if [ "${iQUERY[ID]}" = "" ]; then
if [ -z "${iQUERY[ID]}" ]; then
event_message "${debug}"
else
event_inline "${debug}"
fi
# last count users
# !!!!! DEPRECATED !!!!!
tmpcount="COUNT${CHAT[ID]}"
grep -q "$tmpcount" <"${COUNTFILE}" &>/dev/null || cat <<< "$tmpcount" >>"${COUNTFILE}"
}
@ -431,7 +444,6 @@ event_inline() {
done
}
event_message() {
echo "${MESSAGE[0]}"
local key debug="$1"
# ${MESSAEG[*]} event_message
# shellcheck disable=SC2153
@ -441,7 +453,7 @@ echo "${MESSAGE[0]}"
done
# ${TEXT[*]} event_text
if [ "${MESSAGE[0]}" != "" ]; then
if [ -n "${MESSAGE[0]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_TEXT[@]}"
do
@ -449,7 +461,7 @@ echo "${MESSAGE[0]}"
done
# ${CMD[*]} event_cmd
if [ "${CMD[0]}" != "" ]; then
if [ -n "${CMD[0]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_CMD[@]}"
do
@ -458,7 +470,7 @@ echo "${MESSAGE[0]}"
fi
fi
# ${REPLYTO[*]} event_replyto
if [ "${REPLYTO[UID]}" != "" ]; then
if [ -n "${REPLYTO[UID]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_REPLYTO[@]}"
do
@ -467,7 +479,7 @@ echo "${MESSAGE[0]}"
fi
# ${FORWARD[*]} event_forward
if [ "${FORWARD[UID]}" != "" ]; then
if [ -n "${FORWARD[UID]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_FORWARD[@]}"
do
@ -476,7 +488,7 @@ echo "${MESSAGE[0]}"
fi
# ${CONTACT[*]} event_contact
if [ "${CONTACT[FIRST_NAME]}" != "" ]; then
if [ -n "${CONTACT[FIRST_NAME]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_CONTACT[@]}"
do
@ -486,7 +498,7 @@ echo "${MESSAGE[0]}"
# ${VENUE[*]} event_location
# ${LOCALTION[*]} event_location
if [ "${LOCATION[LONGITUDE]}" != "" ] || [ "${VENUE[TITLE]}" != "" ]; then
if [ -n "${LOCATION[LONGITUDE]}" ] || [ -n "${VENUE[TITLE]}" ]; then
# shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_LOCATION[@]}"
do
@ -538,7 +550,7 @@ process_message() {
# in reply to message from
REPLYTO=( )
REPLYTO[UID]="${UPD["result",${num},"message","reply_to_message","from","id"]}"
if [ "${REPLYTO[UID]}" != "" ]; then
if [ -n "${REPLYTO[UID]}" ]; then
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"]}")"
@ -549,7 +561,7 @@ process_message() {
# forwarded message from
FORWARD=( )
FORWARD[UID]="${UPD["result",${num},"message","forward_from","id"]}"
if [ "${FORWARD[UID]}" != "" ]; then
if [ -n "${FORWARD[UID]}" ]; then
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"]}")"
@ -572,7 +584,7 @@ process_message() {
# Contact
CONTACT=( )
CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","first_name"]}")"
if [ "${CONTACT[FIRST_NAME]}" != "" ]; then
if [ -n "${CONTACT[FIRST_NAME]}" ]; then
CONTACT[USER_ID]="$(JsonDecode "${UPD["result",${num},"message","contact","user_id"]}")"
CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","last_name"]}")"
CONTACT[NUMBER]="${UPD["result",${num},"message","contact","phone_number"]}"
@ -582,7 +594,7 @@ process_message() {
# vunue
VENUE=( )
VENUE[TITLE]="$(JsonDecode "${UPD["result",${num},"message","venue","title"]}")"
if [ "${VENUE[TITLE]}" != "" ]; then
if [ -n "${VENUE[TITLE]}" ]; then
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"]}"
@ -596,9 +608,27 @@ process_message() {
LOCATION[LONGITUDE]="${UPD["result",${num},"message","location","longitude"]}"
LOCATION[LATITUDE]="${UPD["result",${num},"message","location","latitude"]}"
# service messages
SERVICE=( ); NEWMEMBER=( )
SERVICE[NEWMEMBER]="${UPD["result",${num},"message","new_chat_member","id"]}"
if [ -n "${SERVICE[NEWMEMBER]}" ]; then
NEWMEMBER[ID]="${SERVICE[NEWMEMBER]}"
NEWMEMBER[FIRSTNAME]="${UPD["result",${num},"message","new_chat_member","first_name"]}"
NEWMEMBER[LASTNAME]="${UPD["result",${num},"message","new_chat_member","last_name"]}"
NEWMEMBER[USERNAME]="${UPD["result",${num},"message","new_chat_member","username"]}"
NEWMEMBER[ISBOT]="${UPD["result",${num},"message","new_chat_member","is_bot"]}"
fi
SERVICE[LEFTMEMBER]="${UPD["result",${num},"message","left_chat_member","id"]}"
SERVICE[NEWTILE]="${UPD["result",${num},"message","new_chat_title"]}"
SERVICE[NEWPHOTO]="${UPD["result",${num},"message","new_chat_photo"]}"
SERVICE[PINNED]="${UPD["result",${num},"message","pinned_message"]}"
# set SSERVICE to yes if a service message was recieved
[[ "${SERVICE[*]}" =~ ^[[:blank:]]+$ ]] || SERVICE[0]="yes"
# split message in command and args
CMD=( )
if [[ "${MESSAGE[0]}" = "/"* ]]; then
if [[ "${MESSAGE[0]}" == "/"* ]]; then
set -f; unset IFS
# shellcheck disable=SC2206
CMD=( ${MESSAGE[0]} )
@ -616,7 +646,7 @@ start_bot() {
local addsleep="100"
local maxsleep="$(( ${BASHBOT_SLEEP:-5000} + 100 ))"
[[ "${DEBUG}" = *"debug" ]] && exec &>>"DEBUG.log"
[ "${DEBUG}" != "" ] && date && echo "Start BASHBOT in Mode \"${DEBUG}\""
[ -n "${DEBUG}" ] && date && echo "Start BASHBOT in Mode \"${DEBUG}\""
[[ "${DEBUG}" = "xdebug"* ]] && set -x
#cleaup old pipes and empty logfiles
find "${DATADIR}" -type p -delete
@ -626,6 +656,8 @@ start_bot() {
# shellcheck source=./modules/aliases.sh
[ -r "${addons}" ] && source "${addons}" "startbot" "${DEBUG}"
done
# shellcheck source=./commands.sh
source "${COMMANDS}" "startbot"
# start timer events
if _is_function start_timer ; then
# shellcheck disable=SC2064
@ -658,6 +690,7 @@ bot_init() {
# upgrade from old version
local OLDTMP="${BASHBOT_VAR:-.}/tmp-bot-bash"
[ -d "${OLDTMP}" ] && { mv -n "${OLDTMP}/"* "${DATADIR}"; rmdir "${OLDTMP}"; }
# no more existing modules
[ -f "modules/inline.sh" ] && rm -f "modules/inline.sh"
# load addons on startup
for addons in "${ADDONDIR:-.}"/*.sh ; do
@ -668,14 +701,14 @@ bot_init() {
[[ "${UID}" -eq "0" ]] && RUNUSER="nobody"
echo -n "Enter User to run basbot [$RUNUSER]: "
read -r TOUSER
[ "$TOUSER" = "" ] && TOUSER="$RUNUSER"
[ -z "$TOUSER" ] && TOUSER="$RUNUSER"
if ! id "$TOUSER" &>/dev/null; then
echo -e "${RED}User \"$TOUSER\" not found!${NC}"
exit 3
else
# shellcheck disable=SC2009
oldbot="$(ps -fu "$TOUSER" | grep startbot | grep -v -e 'grep' -e '\-startbot' )"
[ "${oldbot}" != "" ] && \
[ -n "${oldbot}" ] && \
echo -e "${ORANGE}Warning: At least one not upgraded TMUX bot is running! You must stop it with kill command:${NC}\\n${oldbot}"
echo "Adjusting user \"${TOUSER}\" files and permissions ..."
[ -w "bashbot.rc" ] && sed -i '/^[# ]*runas=/ s/runas=.*$/runas="'$TOUSER'"/' "bashbot.rc"
@ -707,7 +740,7 @@ fi
if [ "${SOURCE}" != "yes" ] && [ "$1" != "init" ] && [ "$1" != "help" ]; then
ME="$(getBotName)"
if [ "$ME" = "" ]; then
if [ -z "$ME" ]; then
echo -e "${RED}ERROR: Can't connect to Telegram! Your TOKEN is invalid or you are blocked by ${URL%/*} ...${NC}"
case "$1" in
"" | "stop" | "kill"* | "suspendb"* ) # warn, but do not exit
@ -726,10 +759,10 @@ if [ "${SOURCE}" != "yes" ]; then
# internal options only for use from bashbot and developers
case "$1" in
"outproc") # forward output from interactive and jobs to chat
[ "$3" = "" ] && echo "No file to read from" && exit 3
[ "$2" = "" ] && echo "No chat to send to" && exit 3
[ -z "$3" ] && echo "No file to read from" && exit 3
[ -z "$2" ] && echo "No chat to send to" && exit 3
while read -r line ;do
[ "$line" != "" ] && send_message "$2" "$line"
[ -n "$line" ] && send_message "$2" "$line"
done
rm -f -r "${DATADIR:-.}/$3"
[ -s "${DATADIR:-.}/$3.log" ] || rm -f "${DATADIR:-.}/$3.log"
@ -752,11 +785,11 @@ if [ "${SOURCE}" != "yes" ]; then
BOTPID="$(proclist "${SESSION}")"
case "$1" in
"count")
"count") # !!!!! DEPRECATED !!!!!
echo "A total of $(wc -l <"${COUNTFILE}") users used me."
exit
;;
"broadcast")
"broadcast") # !!!!! DEPRECATED !!!!!
NUMCOUNT="$(wc -l <"${COUNTFILE}")"
echo "Sending the broadcast $* to $NUMCOUNT users."
[ "$NUMCOUNT" -gt "300" ] && sleep="sleep 0.5"
@ -764,7 +797,7 @@ if [ "${SOURCE}" != "yes" ]; then
while read -r f; do send_markdown_message "${f//COUNT}" "$*"; $sleep; done <"${COUNTFILE}"
;;
"status")
if [ "${BOTPID}" != "" ]; then
if [ -n "${BOTPID}" ]; then
echo -e "${GREEN}Bot is running.${NC}"
exit
else
@ -775,10 +808,10 @@ if [ "${SOURCE}" != "yes" ]; then
"start")
# shellcheck disable=SC2086
[ "${BOTPID}" != "" ] && kill ${BOTPID}
[ -n "${BOTPID}" ] && kill ${BOTPID}
nohup "$SCRIPT" "startbot" "$2" "${SESSION}" &>/dev/null &
echo "Session Name: ${SESSION}"
if [ "$(proclist "${SESSION}")" != "" ]; then
if [ -n "$(proclist "${SESSION}")" ]; then
echo -e "${GREEN}Bot started successfully.${NC}"
else
echo -e "${RED}An error occurred while starting the bot.${NC}"
@ -786,7 +819,7 @@ if [ "${SOURCE}" != "yes" ]; then
fi
;;
"kill"|"stop")
if [ "${BOTPID}" != "" ]; then
if [ -n "${BOTPID}" ]; then
# shellcheck disable=SC2086
if kill ${BOTPID}; then
echo -e "${GREEN}OK. Bot stopped successfully.${NC}"

View File

@ -1,11 +1,21 @@
#!/bin/bash
# file: commands.sh
# do not edit this file, instead place all your commands in mycommands.sh
# _____ _______ _ _ _
# (____ \ _ (_______) | (_)_ | |
# _ \ \ ___ ____ ___ | |_ _____ _ | |_| |_ | |
# | | | / _ \ | _ \ / _ \| _) | ___) / || | | _)|_|
# | |__/ / |_| | | | | | |_| | |__ | |____( (_| | | |__ _
# |_____/ \___/ |_| |_|\___/ \___) |_______)____|_|\___)_|
#
# this file *MUST* not be edited! palce your config and commands in
# the file "mycommnds.sh". a clean version is provided as "mycommands.clean"
#
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-7-g64efe96
#
# adjust your language setting here, e.g.when run from other user or cron.
@ -17,13 +27,17 @@ export 'LANGUAGE=C.UTF-8'
unset IFS
# set -f # if you are paranoid use set -f to disable globbing
# to change the default info message overwrite bashbot_info in mycommands.sh
#
# this file *MUST* not edited!
# copy "mycommands.sh.dist" to "mycommnds.sh" and change the strings there
bashbot_info='This is bashbot, the Telegram bot written entirely in bash.
It features background tasks and interactive chats, and can serve as an interface for CLI programs.
It currently can send, recieve and forward messages, custom keyboards, photos, audio, voice, documents, locations and video files.
'
# to change the default help messages overwrite in mycommands.sh
#
# this file *MUST* not edited!
# copy "mycommands.sh.dist" to "mycommnds.sh" and change the strings there
bashbot_help='*Available commands*:
*• /start*: _Start bot and get this message_.
*• /help*: _Get this message_.
@ -37,17 +51,22 @@ Get the code in my [GitHub](http://github.com/topkecleon/telegram-bot-bash)
'
# load modues on startup and always on on debug
if [ "${1}" = "source" ] || [[ "${1}" = *"debug"* ]] ; then
# load all readable modules
for modules in "${MODULEDIR:-.}"/*.sh ; do
if [ -n "${1}" ]; then
# load all readable modules
for modules in "${MODULEDIR:-.}"/*.sh ; do
if [[ "${1}" == *"debug"* ]] || ! _is_function "$(basename "${modules}")"; then
# shellcheck source=./modules/aliases.sh
[ -r "${modules}" ] && source "${modules}" "${1}"
done
fi
done
fi
#
# this file *MUST* not edited!
# copy "mycommands.sh.dist" to "mycommnds.sh" and change the values there
# defaults to no inline and nonsense home dir
export INLINE="0"
export FILE_REGEX='/home/user/allowed/.*'
export FILE_REGEX="${BASHBOT_ETC}/.*"
# load mycommands
@ -55,10 +74,10 @@ export FILE_REGEX='/home/user/allowed/.*'
[ -r "${BASHBOT_ETC:-.}/mycommands.sh" ] && source "${BASHBOT_ETC:-.}/mycommands.sh" "${1}"
if [ "${1}" != "source" ];then
if [ -z "${1}" ] || [[ "${1}" == *"debug"* ]];then
# detect inline commands....
# no default commands, all processing is done in myinlines()
if [ "$INLINE" != "0" ] && [ "${iQUERY[ID]}" != "" ]; then
if [ "$INLINE" != "0" ] && [ -n "${iQUERY[ID]}" ]; then
# forward iinline query to optional dispatcher
_exec_if_function myinlines
@ -75,6 +94,9 @@ if [ "${1}" != "source" ];then
if [ "$?" = "0" ]; then
case "${MESSAGE}" in
################################################
# this file *MUST* not edited!
# copy "mycommands.sh.dist" to "mycommnds.sh" and change the values and add your commands there
#
# GLOBAL commands start here, edit messages only
'/info'*)
send_markdown_message "${CHAT[ID]}" "${bashbot_info}"

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# this has to run once atfer git clone
# and every time we create new hooks
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script

View File

@ -3,7 +3,7 @@
#
# works together with git pre-push.sh and ADD all changed files since last push
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
############
# NOTE: you MUST run install-hooks.sh again when updating this file!

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
############
# NOTE: you MUST run install-hooks.sh again when updating this file!

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# this has to run once atfer git clone
# and every time we create new hooks
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script

View File

@ -2,7 +2,7 @@
# file: make-distribution.sh
# creates files and arcchives to dirtribute bashbot
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-12-gb1d569a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script
@ -17,7 +17,7 @@ VERSION="$(git describe --tags | sed -e 's/-[0-9].*//' -e 's/v//')"
DISTNAME="telegram-bot-bash"
DISTDIR="./DIST/${DISTNAME}"
DISTFILES="bashbot.rc bashbot.sh commands.sh mycommands.sh doc examples modules addons LICENSE README.md README.txt README.html"
DISTFILES="bashbot.rc bashbot.sh commands.sh mycommands.sh mycommands.sh.clean doc examples modules addons LICENSE README.md README.txt README.html"
# run tests first!

View File

@ -5,7 +5,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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script

View File

@ -1,3 +1,4 @@
# list of additional files to check from shellcheck
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-7-g64efe96
bashbot.rc
mycommands.sh.clean

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# shellcheck disable=SC2016
#
# Easy Versioning in git:

View File

@ -87,5 +87,5 @@ The old format is supported for backward compatibility, but may fail for corner
#### [Next Create Bot](1_firstbot.md)
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -17,9 +17,13 @@ Have FUN!
### Files
```
.
├── bashbot.sh # main bashbot script - do not edit
├── commands.sh # command dispatcher - do not edit
├── mycommands.sh # place your functions and commands here!
├── mycommands.sh # THIS is your bot, place logic and commands here!
├── mycommands.sh.clean # copy to "mycommands.sh" if you start devloping your bot
├── mycommands.sh.dist # example bot, also used for testing bashbot internally
├── bashbot.sh # main bashbot script - DO NOT EDIT!
├── commands.sh # command dispatcher - DO NOT EDIT!
├── JSON.sh # bashbots JSON parser, see https://github.com/dominictarr/JSON.sh
├── modules # optional functions, sourced by commands.sh
@ -65,16 +69,13 @@ Start or Stop your Bot use the following commands:
```
### User count
To count the total number of users that ever used the bot run the following command:
```bash
./bashbot.sh count
deprecated, will be removed!
```
### Sending broadcasts to all users
To send a broadcast to all of users that ever used the bot run the following command:
```bash
./bashbot.sh broadcast "Hey! I just wanted to let you know that the bot's been updated!"
```
deprecated, will be removed!
----
@ -134,6 +135,22 @@ Evertime a Message is recieved, you can read incoming data using the following v
* ```${VENUE[LONGITUDE]}```: Longitude
* ```${VENUE[LATITUDE]}```: Latitude
* ```${VENUE[FOURSQUARE]}```: Fouresquare ID
* ```$SERVICE```: This array contains info abbout recived service messages.
* ```${SERVICE}```: set to "yes" when a service message is recived.
* ```${SERVICE[NEWMEMBER]```: New user's id
* ```${NEWMEMBER[ID]```: New user's id
* ```${NEWMEMBER[FIRSTNAME]```: New user's first name
* ```${NEWMEMBER[LASTNAME]```: New user's last name
* ```${NEWMEMBER[USERNAME]```: New user's username
* ```${NEWMEMBER[ISBOT]```: New user is a bot
* ```${SERVICE[LEFTMEMBER]```: Id of user left
* ```${SERVICE[NEWTILE]```: Text of new title
* ```${SERVICE[NEWPHOTO]```: New Chat Picture array
* ```${SERVICE[PINNED]```: Pinned Message structure
### Inline queries
Evertime a Message is recieved, you can read incoming data using the following variables:
@ -223,5 +240,5 @@ send_action "${CHAT[ID]}" "action"
#### [Prev Create Bot](1_firstbot.md)
#### [Next Advanced Usage](3_advanced.md)
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -180,5 +180,5 @@ See also [answer_inline_multi, answer_inline_compose](6_reference.md#answer_inli
#### [Prev Getting started](2_usage.md)
#### [Next Expert Use](4_expert.md)
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -348,5 +348,5 @@ for every poll until the maximum of BASHBOT_SLEEP ms.
#### [Prev Advanced Use](3_advanced.md)
#### [Next Best Practice](5_practice.md)
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -152,5 +152,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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -457,17 +457,22 @@ Usually message is automatically forwarded in 'commands.sh', but you can forwar
*usage:* send_interactive "${CHAT[ID]}" "message"
*replaces:*' incproc
*replaces:* incproc
----
### jsshDB
Since output generated by JSON.sh is so handy to use in bash, we use the format for a simple keys/value storage and providing
fucntions to read and write JSON.sh style data from and to files.
The file extions is '.jssh' and for security reasons location of jssh files is restricted to BASHBOT_ETC.
The file extions is '.jssh' and for security reasons location of jssh files is restricted to BASHBOT_ETC and BASHBOT_DATA..
Note: File names containg '..' and absolute file names pointing outside BASHBOT_ETC are refused by jsshDB functions.
Note: File names containg '..' and absolute file names pointing outside BASHBOT_ETC or BASHBOT_DATA are refused by jsshDB functions.
Note2: Since version 0.94 jsshDB functions support file locking with flock. Write / Update operations use flock to wait until
previous operations are finished. see "man flock" for more information. Bashbot uses a maximum timeout of 10 seconds for flock.
If you don't want atomic write / update operations use the *_async variant of jsshDB functions. If flock is not availible
the *_async variant is automatically used.
*Example:* for file name:
```bash
@ -506,19 +511,6 @@ Something wrong with /home/someuser/myfile
Something wrong with data-bot-bash/../../../somevalues
```
##### jssh_readDB
Read content of a .jssh file in JSON.sh format into given ARRAY. ARRAY name must be delared with "declare -A ARRAY" before calling readDB.
*usage:* jssh_readDB "ARRAY" "filename"
*example:*
```bash
# read file data-bot-bash/somevalues.jssh into array SOMEVALUES
jssh_readDB "SOMEVALUES" "${DATADIR:-.}/somevalues"
print "${SOMEVALUES[*]}"
```
##### jssh_writeDB
Write content of an ARRAY into jsshDB file. ARRAY name must be delared with "declare -A ARRAY" before calling writeDB.
"DB" file MUST exist or nothing is written.
@ -527,6 +519,8 @@ Note: Existing content is overwritten.
*usage:* jssh_writeDB "ARRAY" "filename"
*usage:* jssh_writeDB_async "ARRAY" "filename"
*example:*
```bash
# Prepare array to store vaules
@ -550,6 +544,28 @@ cat "${DATADIR:-}/myvalues.jssh"
["value2"] "a value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"
```
##### jssh_printDB
Print content of an ARRAY to STDOUT. ARRAY name must be delared with "declare -A ARRAY" before calling printDB..
*usage:* jssh_printDB "ARRAY"
*example:*
```bash
# Prepare array to store vaules
declare -A PRINTVALUES
# read file data-bot-bash/myvalues.jssh into array READVALUES
jssh_readDB "PRINTVALUES" "${DATADIR:-}/myvalues"
# print DB to stdout
jssh_printDB READVALUES
["value1"] "example"
["value2"] "a value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"```
```
@ -561,6 +577,8 @@ Note: Existing content not in ARRAY is kept in file.
*usage:* jssh_updateDB "ARRAY" "filename"
*usage:* jssh_updateDB_async "ARRAY" "filename"
*example:*
```bash
# continued example from writeDB
@ -586,37 +604,16 @@ cat "$DBfile"
```
##### jssh_insertDB
Insert, update, append a key=value pair to a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
*usage:* jssh_insertDB "key" "value" "filename"
*example:*
```bash
jssh_insertDB "newkey" "an other value" "${DATADIR:-.}/myvalues"
```
##### jssh_getDB
Read a key=value pair from a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
*usage:* jssh_getDB "key" "filename"
*example:*
```bash
result="$(jssh_getDB "newvalue" "${DATADIR:-.}/myvalues")"
# lets see whats the value of key newvalue
echo "$result"
this is new
```
----
##### jssh_readDB
Read content of a file in JSON.sh format into given ARRAY. ARRAY name must be delared with "declare -A ARRAY" upfront,
*usage:* jssh_readDB "ARRAY" "filename"
*usage:* jssh_readDB_async "ARRAY" "filename"
Note: readDB uses concurrent / shared locking from flock so multiple proceses can read from file, as long no process is writing.
Maximum timeour for reading is 1s to not block readers.
*example:*
```bash
# Prepare array to read vaules
@ -627,17 +624,15 @@ jssh_readDB "READVALUES" "${DATADIR:-}/myvalues"
# sinple command to output values ONLY
printf "${READVALUES[*]}"
example a value This is an example content for pseudo multidimensional bash array whynot B whynot A
# function to output key and value
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
printarr READVALUES
value1=example
value2=a value
whynot,subindex2,text=This is an example content for pseudo multidimensional bash array
whynot,subindex2=whynot B
whynot,subindex1=whynot A
# print DB to stdout
jssh_printDB READVALUES
["value1"] "example"
["value2"] "a value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"
# access Arrray
@ -654,18 +649,49 @@ READVALUES["value3"]="new value"
READVALUES[whynot,subindex3]="new subindex value"
# new output
printarr READVALUES
value1=example
value2=this is a changed value
whynot,subindex2,text=This is an example content for pseudo multidimensional bash array
whynot,subindex3=new subindex value
whynot,subindex2=whynot B
whynot,subindex1=whynot A
jssh_printDB READVALUES
["value1"] "example"
["value3"] "new value"
["value2"] "this is a changed value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex3"] "new subindex value"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"
```
##### jssh_insertDB
Insert, update, append a key=value pair to a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
*usage:* jssh_insertDB "key" "value" "filename"
*usage:* jssh_insertDB_asnyc "key" "value" "filename"
Note: insertDB uses also excusiv write locking, but with a maximum timeout of 2s. insertDB is a "fast" operation, simply adding the value to the end of the file.
*example:*
```bash
jssh_insertDB "newkey" "an other value" "${DATADIR:-.}/myvalues"
```
##### jssh_deleteKeyDB
Deleted a key=value pair froma jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
*usage:* jssh_deleteKeyDB "key" "filename"
*usage:* jssh_deleteKeyDB_async "key" "filename"
*example:*
```bash
jssh_deleteKeyDB "delkey"" "${DATADIR:-.}/myvalues"
```
https://linuxhint.com/associative_array_bash/
https://linuxconfig.org/how-to-use-arrays-in-bash-script
----
### Aliases - shortcuts for often used funtions
Aliases are handy shortcuts for using in 'mycommands.sh', they avoid error prone typing of "${CHAT[ID]}" "${USER[ID]}" as much as possible.
Do not use them in bashbot.sh, modules and addons.
@ -947,5 +973,5 @@ The name of your bot is availible as bash variable "$ME", there is no need to ca
#### [Prev Best Practice](5_practice.md)
#### [Next Notes for Developers](7_develop.md)
#### $$VERSION$$ v0.94-dev2-1-g4f90215
#### $$VERSION$$ v0.94-pre-13-g52bde30

View File

@ -7,17 +7,24 @@ If you want to provide fixes or new features [fork bashbot on githup](https://he
### Debugging Bashbot
Usually all bashbot output is discarded.
If you want to get error messages (and more) start bashbot in the current shell with ```./bashbot.sh startbot```.
In addition you can the change the level of verbosity by adding a 'debug' as third argument.
If you want to get error messages (and more) start bashbot ```./bashbot.sh startbot debug```.
you can the change the level of verbosity of the debug argument:
```
"debug" all output is redirected to "DEBUG.log", in addtion every incomming message is logged in "MESSAGE.log" and "INLINE.log"
"xdebug" same as debug plus set bash option '-x' to log any executed command in "DEBUG.log"
use the command tail to watch your bot live, e.g. "tail -f DEBUG.log", to obtain more information place set -x; set +x in your code.
```
```
sometimes its useful to watch the bot live in the terminal:
"debugx" debug output and errors are sent to terminal
"xdebugx" same as debugx plus set bash option '-x' to show any executed command
"debug" all output is redirected to "DEBUG.log", in addtion every incomming message is logged in "MESSAGE.LOG" and "INLINE.log"
"xdebug" same as debug plus set bash option '-x' to log any executed command in "DEBUG.log"
```
To stop bashhbot in debugging mode press CRTL+C. If this does not stop bashbot or you run it in background execute ```ps -uf | grep debug``` and kill all shown processes.
### Modules and Addons
**Modules** resides in ```modules/*.sh``` and are colletions of optional bashbot functions grouped by functionality. Main reason for creating modules was
@ -321,5 +328,5 @@ fi
#### [Prev Function Reference](6_reference.md)
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-0-gac2ec02

View File

@ -55,6 +55,6 @@ convert existing bots.
**external-use** will contain some examples on how to send messages from external scripts to Telegram chats or users.
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

View File

@ -2,7 +2,7 @@
# file: run_filename
# background job to display content of all new files in WATCHDIR
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

View File

@ -2,7 +2,7 @@
# file: run_filename
# background job to display all new files in WATCHDIR
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# 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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
SHELL=/bin/sh

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# adjust your language setting here
# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment

View File

@ -1,7 +1,7 @@
# file: botacl
# a user not listed here, will return false from 'user_is_allowed'
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# Format:
# user:ressource: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$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
#
# shellcheck disable=SC2154
# shellcheck disable=SC2034

View File

@ -5,10 +5,13 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-2-gc0a633f
#
# source from commands.sh to use the aliases
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# easy handling of users:
_is_botadmin() {
user_is_botadmin "${USER[ID]}"

View File

@ -5,10 +5,13 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-2-gc0a633f
# source from commands.sh to use the inline functions
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
INLINE_QUERY=$URL'/answerInlineQuery'
answer_inline_query() {
@ -33,15 +36,15 @@ inline_query_compose(){
JSON='{"type":"article","id":"'$ID'","input_message_content": {"message_text":"'$4'"} '$(title2Json "$3" "" "$5" "$6" "$7")'}'
;;
"photo") # photo ID photoURL (thumbURL title description caption)
[ "$4" = "" ] && tumb="$3"
[ -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)
[ "$4" = "" ] && tumb="$3"
[ -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)
[ "$4" != "" ] && tumb='","thumb_url":"'$4'"'
[ -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)
@ -60,13 +63,13 @@ inline_query_compose(){
JSON='{"type":"location","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}'
;;
"venue") # venue ID lat long title (adress forsquare)
[ "$6" = "" ] && addr="$5"
[ "$7" != "" ] && fours=',"foursquare_id":"'$7'"'
[ -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)
[ "$5" != "" ] && last=',"last_name":"'$5'"'
[ "$6" != "" ] && tumb='","thumb_url":"'$6'"'
[ -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

View File

@ -5,10 +5,13 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-2-gc0a633f
# source from commands.sh if you want ro use interactive or background jobs
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
######
# interactive and background functions
@ -47,7 +50,7 @@ start_back() {
# $2 program
# $3 prefix
start_proc() {
[ "$2" = "" ] && return
[ -z "$2" ] && return
[ -x "${2%% *}" ] || return 1
local fifo; fifo="${DATADIR:-.}/$(procname "$1" "$3")"
kill_proc "$1" "$3"
@ -66,7 +69,7 @@ check_back() {
# $1 chatid
# $2 prefix
check_proc() {
[ "$(proclist "$(procname "$1" "$2")")" != "" ]
[ -n "$(proclist "$(procname "$1" "$2")")" ]
# shellcheck disable=SC2034
res=$?; return $?
}
@ -87,7 +90,7 @@ kill_proc() {
prid="$(proclist "${fifo}")"
fifo="${DATADIR:-.}/${fifo}"
# shellcheck disable=SC2086
[ "${prid}" != "" ] && kill ${prid}
[ -n "${prid}" ] && kill ${prid}
[ -s "${fifo}.log" ] || rm -f "${fifo}.log"
[ -p "${fifo}" ] && rm -f "${fifo}";
}

View File

@ -5,7 +5,10 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-5-g6c6b312
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# source from commands.sh to use the member functions
@ -48,6 +51,7 @@ user_is_admin() {
user_is_botadmin() {
local admin; admin="$(head -n 1 "${BOTADMIN}")"
[ "${admin}" = "${1}" ] && return 0
[ "${admin}" = "${2}" ] && return 0
[[ "${admin}" = "@*" ]] && [[ "${admin}" = "${2}" ]] && return 0
if [ "${admin}" = "?" ]; then printf '%s\n' "${1:-?}" >"${BOTADMIN}"; return 0; fi
return 1
@ -55,10 +59,10 @@ user_is_botadmin() {
user_is_allowed() {
local acl="$1"
[ "$1" = "" ] && return 1
[ -z "$1" ] && return 1
grep -F -xq "${acl}:*:*" <"${BOTACL}" && return 0
[ "$2" != "" ] && acl="${acl}:$2"
[ -n "$2" ] && acl="${acl}:$2"
grep -F -xq "${acl}:*" <"${BOTACL}" && return 0
[ "$3" != "" ] && acl="${acl}:$3"
[ -n "$3" ] && acl="${acl}:$3"
grep -F -xq "${acl}" <"${BOTACL}"
}

View File

@ -5,45 +5,72 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-13-g52bde30
#
# source from commands.sh to use jsonDB functions
#
# jsonDB provides simple functions to read and store bash Arrays
# from to file in JSON.sh output format, its a simple key/value storage.
# read content of a file in JSON.sh format into given ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename, must be relative to BASHBOT_ETC, and not contain '..'
jssh_readDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ "${DB}" = "" ] && return 1
[ ! -f "${DB}" ] && return 2
Json2Array "$1" <"${DB}"
}
# write ARRAY content to a file in JSON.sh format
# Warning: old content is overwritten
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_writeDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ "${DB}" = "" ] && return 1
[ ! -f "${DB}" ] && return 2
Array2Json "$1" >"${DB}"
}
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# new feature: serialize / atomic operations:
# updates will be done atomic with flock
# flock should flock should be availible on all system as its part of busybox
# tinybox
# lockfile filename.flock is persistent and will be testet with flock for active lock (file open)
export BASHBOT_LOCKNAME=".flock"
if _exists flock; then
###############
# we have flock
# use flock for atomic operations
# read content of a file in JSON.sh format into given ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename, must be relative to BASHBOT_ETC, and not contain '..'
jssh_readDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# shared lock, many processes can read, maximum wait 1s
{ flock -s -w 1 200; Json2Array "$1" <"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}"
}
# write ARRAY content to a file in JSON.sh format
# Warning: old content is overwritten
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_writeDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# exclusive lock, no other process can read or write, maximum wait to get lock is 10s
{ flock -e -w 10 200; Array2Json "$1" >"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}"
}
# update/write ARRAY content in file without deleting keys not in ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_updateDB() {
# for atomic update we cant use read/writeDB
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# update/write ARRAY content in file without deleting keys not in ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_updateDB() {
declare -n ARRAY="$1"
[ "${ARRAY[*]}" = "" ] && return 1
[ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR newARR
jssh_readDB "oldARR" "$2" || return "$?"
if [ "${oldARR[*]}" = "" ]; then
# start atomic update here, exclusive max wait 10s
{ flock -e -w 10 200
Json2Array "oldARR" <"${DB}"
if [ -z "${oldARR[*]}" ]; then
# no old content
jssh_writeDB "$1" "$2"
Array2Json "$1" >"${DB}"
else
# merge arrays
local o1 o2 n1 n2
@ -53,23 +80,78 @@ jssh_updateDB() {
#shellcheck disable=SC2034,SC2190,SC2206
newARR=( ${o2:0:${#o2}-1} ${n2:0:${#n2}-1} )
set +f
jssh_writeDB "newARR" "$2"
Array2Json "newARR" >"${DB}"
fi
}
} 200>"${DB}${BASHBOT_LOCKNAME}"
}
# insert, update, apped key/value to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_insertDB() {
# insert, update, apped key/value to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_insertDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")"
[ "${DB}" = "" ] && return 1
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# its append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}"
# start atomic update here, exclusive max wait 2si, it's append, not overwrite
{ flock -e -w 2 200
# it's append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}"
} 200>"${DB}${BASHBOT_LOCKNAME}"
}
# delete key/value from jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_deleteKeyDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
declare -A oldARR
# start atomic delete here, exclusive max wait 10s
{ flock -e -w 10 200
Json2Array "oldARR" <"${DB}"
if [[ -v "oldAR[$1]" ]] ; then
unset oldARR["$1"]
Array2Json "oldARR" >"${DB}"
fi
} 200>"${DB}${BASHBOT_LOCKNAME}"
}
else
#########
# we have no flock, use "old" not atomic functions
jssh_readDB() {
jssh_readDB_async "$@"
}
jssh_writeDB() {
jssh_writeDB_async "$@"
}
jssh_updateDB() {
jssh_updateDB_async "$@"
}
jssh_insertDB() {
jssh_insertDB_async "$@"
}
jssh_deleteKeyDB() {
jssh_deleteKeyDB_async "$@"
}
fi
##############
# no need for atomic
# print ARRAY content to stdout instead of file
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
jssh_printDB() {
Array2Json "$1"
}
# get key/value from jsshDB
@ -87,7 +169,7 @@ jssh_getDB() {
# $1 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_newDB() {
local DB; DB="$(jssh_checkDB "$1")"
[ "${DB}" = "" ] && return 1
[ -z "${DB}" ] && return 1
[ -f "${DB}" ] && return 2 # already exist, do not zero out
printf '\n' >"${DB}"
}
@ -95,10 +177,72 @@ jssh_newDB() {
# $1 filename, check filename, it must be relative to BASHBOT_ETC, and not contain '..'
# returns real path to DB file if everything is ok
jssh_checkDB(){
[ "$1" = "" ] && return 1
[ -z "$1" ] && return 1
local DB="${BASHBOT_ETC:-.}/$1.jssh"
[[ "$1" = "${BASHBOT_ETC:-.}"* ]] && DB="$1.jssh"
if [[ "$1" = "${BASHBOT_ETC:-.}"* ]] || [[ "$1" = "${BASHBOT_DATA:-.}"* ]]; then
DB="$1.jssh"
fi
[[ "$1" = *'..'* ]] && return 2
printf '%s\n' "${DB}"
}
######################
# "old" implementations as non atomic functions
# can be used explictitly or as fallback if flock is not availible
jssh_readDB_async() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
Json2Array "$1" <"${DB}"
}
jssh_writeDB_async() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
Array2Json "$1" >"${DB}"
}
jssh_updateDB_async() {
declare -n ARRAY="$1"
[ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR newARR
jssh_readDB_async "oldARR" "$2" || return "$?"
if [ -z "${oldARR[*]}" ]; then
# no old content
jssh_writeDB_async "$1" "$2"
else
# merge arrays
local o1 o2 n1 n2
o1="$(declare -p oldARR)"; o2="${o1#*\(}"
n1="$(declare -p ARRAY)"; n2="${n1#*\(}"
unset IFS; set -f
#shellcheck disable=SC2034,SC2190,SC2206
newARR=( ${o2:0:${#o2}-1} ${n2:0:${#n2}-1} )
set +f
jssh_writeDB_async "newARR" "$2"
fi
}
jssh_insertDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# its append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}"
}
jssh_deleteKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
declare -A oldARR
jssh_readDB_async "oldARR" "$2" || return "$?"
if [[ -v "oldAR[$1]" ]] ; then
unset oldARR["$1"]
jssh_writeDB_async "oldARR" "$2"
fi
}

View File

@ -5,7 +5,10 @@
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-8-g284172f
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# source from commands.sh to use the sendMessage functions
@ -22,7 +25,7 @@ ACTION_URL=$URL'/sendChatAction'
FORWARD_URL=$URL'/forwardMessage'
send_normal_message() {
local text="$(JsonEscape "${2}")"
local text; text="$(JsonEscape "${2}")"
until [ -z "${text}" ]; do
sendJson "${1}" '"text":"'"${text:0:4096}"'"' "${MSG_URL}"
text="${text:4096}"
@ -30,7 +33,7 @@ send_normal_message() {
}
send_markdown_message() {
local text="$(JsonEscape "${2}")"
local text; text="$(JsonEscape "${2}")"
until [ -z "${text}" ]; do
sendJson "${1}" '"text":"'"${text:0:4096}"'","parse_mode":"markdown"' "${MSG_URL}"
text="${text:4096}"
@ -38,7 +41,7 @@ send_markdown_message() {
}
send_html_message() {
local text="$(JsonEscape "${2}")"
local text; text="$(JsonEscape "${2}")"
until [ -z "${text}" ]; do
sendJson "${1}" '"text":"'"${text:0:4096}"'","parse_mode":"html"' "${MSG_URL}"
text="${text:4096}"
@ -46,7 +49,7 @@ send_html_message() {
}
old_send_keyboard() {
local text='"text":"'$(JsonEscape "${2}")'"'
local text; text='"text":"'$(JsonEscape "${2}")'"'
shift 2
local keyboard="init"
OLDIFS="$IFS"
@ -64,24 +67,24 @@ sendEmpty() {
}
send_keyboard() {
if [[ "$3" != *'['* ]]; then old_send_keyboard "${@}"; return; fi
local text='"text":"'$(JsonEscape "${2}")'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"'
local one_time=', "one_time_keyboard":true' && [ "$4" != "" ] && one_time=""
local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"${ISEMPTY}"'"'
local one_time=', "one_time_keyboard":true' && [ -n "$4" ] && one_time=""
sendEmpty "${1}" "${text}"', "reply_markup": {"keyboard": [ '"${3}"' ] '"${one_time}"'}' "$MSG_URL"
# '"text":"$2", "reply_markup": {"keyboard": [ ${3} ], "one_time_keyboard": true}'
}
remove_keyboard() {
local text='"text":"'$(JsonEscape "${2}")'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"'
local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"${ISEMPTY}"'"'
sendEmpty "${1}" "${text}"', "reply_markup": {"remove_keyboard":true}' "$MSG_URL"
#JSON='"text":"$2", "reply_markup": {"remove_keyboard":true}'
}
send_inline_keyboard() {
local text='"text":"'$(JsonEscape "${2}")'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"'
local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"${ISEMPTY}"'"'
sendEmpty "${1}" "${text}"', "reply_markup": {"inline_keyboard": [ '"${3}"' ]}' "$MSG_URL"
# JSON='"text":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}'
}
send_button() {
send_inline_keyboard "${1}" "${2}" '[ {"text":"'$(JsonEscape "${3}")'", "url":"'"${4}"'"}]'
send_inline_keyboard "${1}" "${2}" '[ {"text":"'"$(JsonEscape "${3}")"'", "url":"'"${4}"'"}]'
}
@ -90,7 +93,7 @@ UPLOADDIR="${BASHBOT_UPLOAD:-${DATADIR}/upload}"
# for now this can only send local files with curl!
# extend to allow send files by URL or telegram ID
send_file() {
[ "$2" = "" ] && return
[ -z "$2" ] && return
[[ "$2" = "http"* ]] && return # currently we do not support URL
upload_file "${@}"
}
@ -114,7 +117,7 @@ upload_file(){
WHAT="audio"
STATUS="upload_audio"
;;
png|jpg|jpeg|gif)
png|jpg|jpeg|gif|pic)
CUR_URL="$PHO_URL"
WHAT="photo"
STATUS="upload_photo"
@ -147,25 +150,25 @@ upload_file(){
# 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 location
send_action() {
[ "$2" = "" ] && return
sendJson "${1}" '"action": "'"${2}"'"' "$ACTION_URL"
[ -z "$2" ] && return
sendJson "${1}" '"action": "'"${2}"'"' "$ACTION_URL" &
}
send_location() {
[ "$3" = "" ] && return
[ -z "$3" ] && return
sendJson "${1}" '"latitude": '"${2}"', "longitude": '"${3}"'' "$LOCATION_URL"
}
send_venue() {
local add=""
[ "$5" = "" ] && return
[ "$6" != "" ] && add=', "foursquare_id": '"$6"''
[ -z "$5" ] && return
[ -n "$6" ] && add=', "foursquare_id": '"$6"''
sendJson "${1}" '"latitude": '"${2}"', "longitude": '"${3}"', "address": "'"${5}"'", "title": "'"${4}"'"'"${add}" "$VENUE_URL"
}
forward_message() {
[ "$3" = "" ] && return
[ -z "$3" ] && return
sendJson "${1}" '"from_chat_id": '"${2}"', "message_id": '"${3}"'' "$FORWARD_URL"
}
forward() { # backward compatibility
@ -173,7 +176,7 @@ forward() { # backward compatibility
}
send_message() {
[ "$2" = "" ] && return
[ -z "$2" ] && return
local text keyboard btext burl no_keyboard file lat long title address sent
text="$(sed <<< "${2}" 's/ mykeyboardend.*//;s/ *my[kfltab][a-z]\{2,13\}startshere.*//')$(sed <<< "${2}" -n '/mytextstartshere/ s/.*mytextstartshere//p')"
text="$(sed <<< "${text}" 's/ *mynewlinestartshere */\r\n/g')"
@ -188,27 +191,27 @@ send_message() {
title="$(sed <<< "${2}" '/mytitlestartshere /!d;s/.*mytitlestartshere //;s/ *my[kfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')"
address="$(sed <<< "${2}" '/myaddressstartshere /!d;s/.*myaddressstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')"
}
if [ "$no_keyboard" != "" ]; then
if [ -n "$no_keyboard" ]; then
remove_keyboard "$1" "$text"
sent=y
fi
if [ "$keyboard" != "" ]; then
if [ -n "$keyboard" ]; then
if [[ "$keyboard" != *"["* ]]; then # pre 0.60 style
keyboard="[ ${keyboard//\" \"/\" \] , \[ \"} ]"
fi
send_keyboard "$1" "$text" "$keyboard"
sent=y
fi
if [ "$btext" != "" ] && [ "$burl" != "" ]; then
if [ -n "$btext" ] && [ -n "$burl" ]; then
send_button "$1" "$text" "$btext" "$burl"
sent=y
fi
if [ "$file" != "" ]; then
if [ -n "$file" ]; then
send_file "$1" "$file" "$text"
sent=y
fi
if [ "$lat" != "" ] && [ "$long" != "" ]; then
if [ "$address" != "" ] && [ "$title" != "" ]; then
if [ -n "$lat" ] && [ -n "$long" ]; then
if [ -n "$address" ] && [ -n "$title" ]; then
send_venue "$1" "$lat" "$long" "$title" "$address"
else
send_location "$1" "$lat" "$long"

View File

@ -1,16 +1,22 @@
#!/bin/bash
# files: mycommands.sh.dist
# copy to mycommands.sh and add all your commands and functions here ...
#########
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
# files: mycommands.sh.dist
#
# this is an out of the box test and example file to show what you can do in mycommands.sh
#
# #### if you start to develop your own bot, use the clean version of this file:
# #### mycommands.clean
#
#### $$VERSION$$ v0.94-pre-7-g64efe96
#
# uncomment the following lines to overwrite info and help messages
# bashbot_info='This is bashbot, the Telegram bot written entirely in bash.
# export bashbot_info='This is bashbot, the Telegram bot written entirely in bash.
#'
# bashbot_help='*Available commands*:
# export bashbot_help='*Available commands*:
#'
res=""
export res=""
# Set INLINE to 1 in order to receive inline queries.
# To enable this option in your bot, send the /setinline command to @BotFather.
@ -19,15 +25,44 @@ export INLINE="0"
# NOTE: this is a regex, not shell globbing! you must use a valid egex,
# '.' matches any charater and '.*' matches all remaining charatcers!
# additionally you must escape special charaters with '\', e.g. '\. \? \[ \*" to match them literally
export FILE_REGEX='^/home/user/allowed/.*'
export FILE_REGEX="${BASHBOT_ETC}/.*"
# example: run bashbot over TOR
# export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1:9050"
if [ "$1" != "source" ];then
# your additional bahsbot commands
# set to "yes" and give your bot admin privilegs to remove service messaes from groups
export SILENCER="no"
if [ "$1" = "startbot" ];then
###################
# this function is run once after startup when the first message is recieved
my_startup(){
# send message ito first user on startup
send_normal_message "${CHAT[ID]}" "Hi, you was the first one after startup!"
}
# reminde bot that it was started
touch .mystartup
else
# here we call the function above when the mesage arrives
# things to do only at soure, eg. after startup
[ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup
#############################
# your own bashbot commands
# NOTE: command can have @botname attached, you must add * in case tests...
mycommands() {
##############
# a service Message was recieved
# add your own stuff here
if [ -n "${SERVICE}" ]; then
# example: delete every service message
if [ "${SILENCER}" = "yes" ]; then
delete_message "${CHAT[ID]}" "${MESSAGE[ID]}"
fi
fi
case "${MESSAGE}" in
##################
# example commands, replace thm by your own

123
mycommands.sh.clean Normal file
View File

@ -0,0 +1,123 @@
#!/bin/bash
########
#
# files: mycommands.sh.clean
# copy to mycommands.sh and add all your commands and functions here ...
#
#### $$VERSION$$ v0.94-pre-7-g64efe96
#
##########
# edit the following lines to fit your bot usage
export bashbot_info='This is bashbot, the Telegram bot written entirely in bash.
'
export bashbot_help='*Available commands*:
'
export res=""
# Set INLINE to 1 in order to receive inline queries.
# To enable this option in your bot ypu must also send the /setinline command to @BotFather.
export INLINE="0"
# NOTE: this is a regex, not shell globbing! you must use a valid egex,
# '.' matches any charater and '.*' matches all remaining charatcers!
# additionally you must escape special charaters with '\', e.g. '\. \? \[ \*" to match them literally
# do NOT set to .* as this allow sending files from all locations!
export FILE_REGEX="${BASHBOT_ETC}/.*"
# set to "yes" and give your bot admin privilegs to remove service messaes from groups
export SILENCER="no"
########
# special network setups may require to provide additional ARGS to curl
#
# example: run bashbot over TOR or SOCKS proxy
# export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1:9050" # TOR
# export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1" # regular SOCKS
if [ "$1" = "startbot" ];then
# run once after startup when the first message is recieved
my_startup(){
:
}
touch .mystartup
else
# here we call the function above when the mesage arrives
# things to do only at soure, eg. after startup
[ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup
#############################
# your own bashbot commands
# NOTE: command can have @botname attached, you must add * in case tests...
mycommands() {
##############
# a service Message was recieved
# add your own stuff here
if [ -n "${SERVICE}" ]; then
# example: delete every service message
if [ "${SILENCER}" = "yes" ]; then
delete_message "${CHAT[ID]}" "${MESSAGE[ID]}"
fi
fi
case "${MESSAGE}" in
##################
# example command, replace them by your own
'/echo'*) # example echo command
send_normal_message "${CHAT[ID]}" "$MESSAGE"
;;
##########
# command overwrite examples
# return 0 -> run default command afterwards
# return 1 -> skip possible default commands
'/info'*) # output date in front of regular info
send_normal_message "${CHAT[ID]}" "$(date)"
return 0
;;
'/kickme'*) # this will replace the /kickme command
send_markdown_mesage "${CHAT[ID]}" "*This bot will not kick you!*"
return 1
;;
esac
}
myinlines() {
#######################
# this fuinction is called only if you has set INLINE=1 !!
# shellcheck disable=SC2128
iQUERY="${iQUERY,,}"
case "${iQUERY}" in
##################
# example inline command, replace it by your own
"image "*) # search images with yahoo
local search="${iQUERY#* }"
answer_inline_multi "${iQUERY[ID]}" "$(my_image_search "${search}")"
;;
esac
}
#####################
# place your processing functions here
# example inline processing function, not really useful
# $1 search parameter
my_image_search(){
local image result sep="" count="1"
result="$(wget --user-agent 'Mozilla/5.0' -qO - "https://images.search.yahoo.com/search/images?p=$1" | sed 's/</\n</g' | grep "<img src=")"
while read -r image; do
[ "$count" -gt "20" ] && break
image="${image#* src=\'}"; image="${image%%&pid=*}"
[[ "${image}" = *"src="* ]] && continue
echo "${sep}"; inline_query_compose "$RANDOM" "photo" "${image}"; sep=","
count=$(( count + 1 ))
done <<<"${result}"
}
fi

View File

@ -2,7 +2,7 @@
#
# ADD a new test skeleton to test dir, but does not activate test
#
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-4-gd28f975
# common variables
export TESTME DIRME TESTDIR LOGFILE REFDIR TESTNAME

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
../dev/hooks/pre-commit.sh

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# file: b-example-test.sh
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-4-gd28f975
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-10-g23a3d4b
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-pre-10-g23a3d4b
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,4 +1,4 @@
chat:123456 JSON:"text":"# test for text only output"
chat:123456 JSON:"text":"\# test for text only output"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":"This is a normal text"
@ -8,21 +8,21 @@ chat:123456 JSON:"text":"This is a normal text
with a line break"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":" This is a <b>HTML</b> text","parse_mode":"html"
chat:123456 JSON:"text":" This is a <b>HTML<\/b> text","parse_mode":"html"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":" This is a <b>HTML</b> text
chat:123456 JSON:"text":" This is a <b>HTML<\/b> text
with a line break","parse_mode":"html"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":" This is a *MARKDOWN* text","parse_mode":"markdown"
chat:123456 JSON:"text":" This is a \*MARKDOWN\* text","parse_mode":"markdown"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":" This is a *MARKDOWN* text
chat:123456 JSON:"text":" This is a \*MARKDOWN\* text
with a line break","parse_mode":"markdown"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":"# test for keyboard, file, venue output"
chat:123456 JSON:"text":"\# test for keyboard\, file\, venue output"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"text":"Text plus keyboard will appear in chat", "reply_markup": {"keyboard": [ [ "Yep, sure" , "No, highly unlikely" ] ] , "one_time_keyboard":true}
@ -34,31 +34,31 @@ URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashb
chat:123456 JSON:"latitude": la10, "longitude": lo20, "address": "Diagon Alley N. 37", "title": "my home"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendVenue
chat:123456 JSON:"text":"# test for new inline button"
chat:123456 JSON:"text":"\# test for new inline button"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
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?bashbottestscript/sendMessage
chat:123456 JSON:"text":"STABILO 88/240 Fineliner point 88
chat:123456 JSON:"text":"STABILO 88\/240 Fineliner point 88
[https://images-na.ssl-images-amazon.com/images/I/41oypA3kmHL.l_SX300.jpg]
[https:\/\/images\-na.ssl\-images\-amazon.com\/images\/I\/41oypA3kmHL.l_SX300.jpg]
second part of text
plus 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?bashbottestscript/sendMessage
chat:123456 JSON:"text":"# test for sendfile"
chat:123456 JSON:"text":"\# test for sendfile"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendMessage
chat:123456 JSON:"action": "upload_photo"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendChatAction
chat:123456 JSON:"photo":"/tmp/allowed/this_is_my.gif","caption":"Text plus absolute file will appear in chat"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendPhoto
chat:123456 JSON:"action": "upload_document"
chat:123456 JSON:"action": "upload_photo"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendChatAction
chat:123456 JSON:"document":"/tmp/allowed/this_is_my.doc","caption":"Text plus absolute file will appear in chat"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendDocument
chat:123456 JSON:"action": "upload_document"
URL:https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/getMe?bashbottestscript/sendChatAction

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
#### $$VERSION$$ v0.94-dev2-0-g3d636f7
#### $$VERSION$$ v0.94-dev3-0-geef955a
# include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh