Skip to content

Commit 94aefbe

Browse files
authored
Merge pull request #177 from topkecleon/develop
Prepare 1.45 release with final webhook and get-file support
2 parents aee7bae + 369124b commit 94aefbe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+538
-277
lines changed

README.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
pre > code.sourceCode { white-space: pre; position: relative; }
146146
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
147147
pre > code.sourceCode > span:empty { height: 1.2em; }
148+
.sourceCode { overflow: visible; }
148149
code.sourceCode > span { color: inherit; text-decoration: inherit; }
149150
div.sourceCode { margin: 1em 0; }
150151
pre.sourceCode { margin: 0; }
@@ -341,7 +342,7 @@ <h2>Security Considerations</h2>
341342
<p>Running a Telegram Bot means it is connected to the public and you never know what's send to your Bot.</p>
342343
<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>
343344
<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>
344-
<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>
345+
<p>It's important to escape or remove <code>$</code> and ` 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>${var}</code>, <code>$(cmd)</code> and `cmd` can lead to remote code execution (RCE) or remote information disclosure (RID) bugs if unescaped <code>$</code> or ` is included in untrusted input (e.g. <code>$$</code> or <code>$(rm -rf /*)</code>).</p>
345346
<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>
346347
<h3>Use printf whenever possible</h3>
347348
<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>
@@ -392,6 +393,6 @@ <h3>Blocked by telegram?</h3>
392393
<p>@Gnadelwartz</p>
393394
<h2>That's it all guys!</h2>
394395
<p>If you feel that there's something missing or if you found a bug, feel free to submit a pull request!</p>
395-
<h4>$$VERSION$$ v1.41-0-gad1b91f</h4>
396+
<h4>$$VERSION$$ v1.5-0-g8adca9b</h4>
396397
</body>
397398
</html>

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ Whenever you are processing input from untrusted sources (messages, files, netwo
146146
(e.g. set IFS appropriately, disable globbing with `set -f` and quote everything). In addition remove unused scripts and examples
147147
from your Bot (e.g. everything in `example/`) and disable/remove all unused bot commands.
148148

149-
It's important to escape or remove `$` in input from user, files or network (_as bashbot does_).
150-
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 /*)`).
149+
It's important to escape or remove `$` and \` in input from user, files or network (_as bashbot does_).
150+
One of the powerful features of Unix shells is variable and command substitution using `${var}`, `$(cmd)` and \`cmd\` can lead to remote
151+
code execution (RCE) or remote information disclosure (RID) bugs if unescaped `$` or \` is included in untrusted input (e.g. `$$` or `$(rm -rf /*)`).
151152

152153
A powerful tool to improve your scripts is `shellcheck`. You can [use it online](https://www.shellcheck.net/) or
153154
[install shellcheck locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used extensively in bashbot development
@@ -241,4 +242,4 @@ See `mycommnds.sh.dist` for an example.
241242

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

244-
#### $$VERSION$$ v1.41-0-gad1b91f
245+
#### $$VERSION$$ v1.5-0-g8adca9b

README.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,11 @@ Whenever you are processing input from untrusted sources (messages, files, netwo
200200
must be as careful as possible (e.g. set IFS appropriately, disable globbing with set -
201201
f and quote everything). In addition remove unused scripts and examples from your Bot
202202
(e.g. everything in example/) and disable/remove all unused bot commands.
203-
It's important to escape or remove $ in input from user, files or network (as bashbot
204-
does). One of the powerful features of Unix shells is variable and command substitution
205-
using ${} and $() can lead to remote code execution (RCE) or remote information disclosure
206-
(RID) bugs if unescaped $ is included in untrusted input (e.g. $$ or $(rm -rf /*)).
203+
It's important to escape or remove $ and ` in input from user, files or network (as
204+
bashbot does). One of the powerful features of Unix shells is variable and command
205+
substitution using ${var}, $(cmd) and `cmd` can lead to remote code execution (RCE) or
206+
remote information disclosure (RID) bugs if unescaped $ or ` is included in untrusted
207+
input (e.g. $$ or $(rm -rf /*)).
207208
A powerful tool to improve your scripts is shellcheck. You can use it online [https://
208209
www.shellcheck.net/] or install shellcheck locally [https://github.com/koalaman/
209210
shellcheck#installing]. Shellcheck is used extensively in bashbot development to ensure a
@@ -318,5 +319,5 @@ That's it all guys!
318319
If you feel that there's something missing or if you found a bug, feel free to submit a
319320
pull request!
320321

321-
$$VERSION$$ v1.41-0-gad1b91f
322+
$$VERSION$$ v1.5-0-g8adca9b
322323

addons/antiFlood.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# this addon counts how many files, e.g. stickers, are sent to
55
# a chat and takes actions if threshold is reached
66
#
7-
#### $$VERSION$$ v1.40-0-gf9dab50
7+
#### $$VERSION$$ v1.5-0-g8adca9b
88

99
# used events:
1010
#

addons/example.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Addons can register to bashbot events at startup
55
# by providing their name and a callback per event
66
#
7-
#### $$VERSION$$ v1.40-0-gf9dab50
7+
#### $$VERSION$$ v1.5-0-g8adca9b
88
#
99
# If an event occurs each registered event function is called.
1010
#

bashbot.rc

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
#
66
# tested on: ubuntu, opensuse, debian
77
#
8-
#### $$VERSION$$ v1.40-0-gf9dab50
8+
#### $$VERSION$$ v1.5-0-g8adca9b
99
# shellcheck disable=SC2009
1010
# shellcheck disable=SC2181
11+
# shellcheck disable=SC2250
1112

1213
#
1314
### BEGIN INIT INFO
@@ -31,13 +32,17 @@ runas="nobody"
3132

3233
# uncomment one of the example lines to fit your system
3334
# runcmd="su ${runas} -s /bin/bash -c " # runasuser with *su*
34-
# runcmd="runuser ${runas} -s /bin/bash -c " # runasuser with *runuser*
35+
# runcmd="/usr/sbin/runuser ${runas} -s /bin/bash -c " # runasuser with *runuser*
3536

3637
# edit the values of the following lines to fit your config:
37-
# your bot installation dir
38-
bashbot="cd /usr/local/telegram-bot-bash; /usr/local/telegram-bot-bash/bashbot.sh"
3938
# your bot name as given to botfather, e.g. mysomething_bot
4039
name=""
40+
# your bot installation dir
41+
bashbotdir="/usr/local/telegram-bot-bash"
42+
databotdir="${bashbotdir}/data-bot-bash"
43+
# programs to run
44+
bashbot="cd ${bashbotdir}; ${bashbotdir}/bashbot.sh"
45+
webhook="cd ${bashbotdir}; nohup ${bashbotdir}/bin/process_batch.sh --startbot --watch ${databotdir}/webhook-fifo-${name}"
4146
# set additionl parameter, e.g. debug
4247
mode=""
4348

@@ -48,29 +53,54 @@ mode=""
4853

4954
case "$1" in
5055
'start')
51-
# shellcheck disable=SC2250
5256
$runcmd "$bashbot start $mode" # >/dev/null 2>&1 </dev/null
5357
RETVAL=$?
5458
;;
59+
'starthook')
60+
printf "Starting bashbot in webhook mode ... "
61+
$runcmd "$webhook $mode </dev/null &>>${bashbotdir}/logs/WEBHOOK.log &" # >/dev/null 2>&1 </dev/null
62+
sleep 1
63+
$0 status
64+
RETVAL=$?
65+
;;
5566
'stop')
56-
# shellcheck disable=SC2250
5767
$runcmd "$bashbot stop $mode"
5868
RETVAL=$?
5969
;;
70+
'stophook')
71+
printf "Stopping bashbot webhook mode ... "
72+
KILLID="$(ps -f -u "${runas}" | grep "process_batch.sh --startbot" | sed -E 's/[^0-9]+([0-9]+).*/\1/')"
73+
if [ -n "${KILLID}" ]; then
74+
$runcmd "kill $(printf "%s" "${KILLID}" | tr -s "\r\n" " " )"
75+
sleep 1
76+
$0 status
77+
fi
78+
RETVAL=$?
79+
;;
6080
'status')
6181
ps -f -u "${runas}" | grep "${name}" | grep -qF "bashbot.sh startbot"
6282
if [ "$?" = "0" ]; then
63-
printf "bashbot (%s) is running\n" "${name}"
83+
printf "bashbot (%s) is running in poll mode\n" "${name}"
6484
RETVAL=0
6585
else
66-
printf "bashbot (%s) is stopped\n" "${name}"
67-
RETVAL=1
86+
ps -f -u "${runas}" | grep "${name}" | grep -qF "process_batch.sh --startbot"
87+
if [ "$?" = "0" ]; then
88+
printf "bashbot (%s) is running in webhook mode\n" "${name}"
89+
RETVAL=0
90+
else
91+
printf "bashbot (%s) is stopped\n" "${name}"
92+
RETVAL=1
93+
fi
6894
fi
6995
;;
7096
'restart'|'reload')
7197
$0 stop; $0 start
7298
RETVAL=$?
7399
;;
100+
'restarthook'|'reloadhook')
101+
$0 stophook; $0 starthook
102+
RETVAL=$?
103+
;;
74104
'restartback')
75105
$0 suspendback; $0 resumeback
76106
RETVAL=$?
@@ -86,7 +116,8 @@ case "$1" in
86116
fi
87117
;;
88118
*)
89-
printf "%s\n" "Usage: $0 { start | stop | restart | reload | restartback | suspendback | resumeback | killback }"
119+
printf "%s\n" "Usage: $0 [ start | stop | restart | starthook | stophook | restarthook ]"
120+
printf "%s\n" " $0 [ status | restartback | suspendback | resumeback | killback ]"
90121
RETVAL=1
91122
;;
92123
esac

bashbot.sh

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ BOTCOMMANDS="-h help init start stop status suspendback resumeback killb
3030
# 8 - curl/wget missing
3131
# 10 - not bash!
3232
#
33-
#### $$VERSION$$ v1.45-dev-26-g82a57a7
33+
#### $$VERSION$$ v1.5-0-g8adca9b
3434
##################################################################
3535

3636
# are we running in a terminal?
@@ -106,7 +106,7 @@ JsonEscape(){
106106
}
107107
# clean \ from escaped json string
108108
# $1 string, output cleaned string
109-
cleanEscaped(){ # remove " all \ but \n\u \n or \r
109+
cleanEscape(){ # remove " all \ but \n\u \n or \r
110110
sed -E -e 's/\\"/+/g' -e 's/\\([^nu])/\1/g' -e 's/(\r|\n)//g' <<<"$1"
111111
}
112112
# check if $1 seems a valid token
@@ -358,7 +358,7 @@ declare -rx BOTTOKEN URL ME_URL
358358
declare -ax CMD
359359
declare -Ax UPD BOTSENT USER MESSAGE URLS CONTACT LOCATION CHAT FORWARD REPLYTO VENUE iQUERY iBUTTON
360360
declare -Ax SERVICE NEWMEMBER LEFTMEMBER PINNED MIGRATE
361-
export res CAPTION ME
361+
export res CAPTION ME BOTADMIN
362362

363363

364364
###############
@@ -491,9 +491,8 @@ sendJson(){
491491
# compose final json
492492
json='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<"$2")"'}'
493493
if [ -n "${BASHBOTDEBUG}" ] ; then
494-
log_update "sendJson (${DETECTED_CURL}) CHAT=${chat#*:} JSON=${2:0:100} URL=${3##*/}"
495-
# mask " and \ , remove newline from json
496-
log_message "DEBUG sendJson ==========\n$("${JSONSHFILE}" -b -n <<<"$(cleanEscaped "${json}")" 2>&1)"
494+
log_update "sendJson (${DETECTED_CURL}) CHAT=${chat#*:} JSON=$(cleanEscape "${json:0:100}") URL=${3##*/}"
495+
log_message "DEBUG sendJson ==========\n$("${JSONSHFILE}" -b -n <<<"$(cleanEscape "${json}")" 2>&1)"
497496
fi
498497
# chat id not a number
499498
if [[ "${chat}" == *"NAN\"," ]]; then
@@ -515,8 +514,8 @@ UPLOADDIR="${BASHBOT_UPLOAD:-${DATADIR}/upload}"
515514
# return final file name or empty string on error
516515
checkUploadFile() {
517516
local err file="$2"
518-
[[ "${file}" = *'..'* || "${file}" = '.'* ]] && err=1 # no directory traversal
519-
if [[ "${file}" = '/'* ]] ; then
517+
[[ "${file}" == *'..'* || "${file}" == '.'* ]] && err=1 # no directory traversal
518+
if [[ "${file}" == '/'* ]] ; then
520519
[[ ! "${file}" =~ ${FILE_REGEX} ]] && err=2 # absolute must match REGEX
521520
else
522521
file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # others must be in UPLOADDIR
@@ -537,6 +536,7 @@ checkUploadFile() {
537536
[ -n "${BASHBOTDEBUG}" ] && log_debug "$3: CHAT=$1 FILE=$2 MSG=${BOTSENT[DESCRIPTION]}"
538537
return 1
539538
fi
539+
printf "%s\n" "${file}"
540540
}
541541

542542

@@ -747,6 +747,16 @@ event_send() {
747747
done
748748
}
749749

750+
# cleanup activities on startup, called from startbot and resume background jobs
751+
# $1 action, timestamp for action is saved in config
752+
bot_cleanup() {
753+
# cleanup countfile on startup
754+
jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" "${COUNTFILE}"
755+
[ -f "${COUNTFILE}.jssh.flock" ] && rm -f "${COUNTFILE}.jssh.flock"
756+
# store action time and cleanup botconfig on startup
757+
[ -n "$1" ] && jssh_updateKeyDB "$1" "$(_date)" "${BOTCONFIG}"
758+
[ -f "${BOTCONFIG}.jssh.flock" ] && rm -f "${BOTCONFIG}.jssh.flock"
759+
}
750760

751761
# fallback version, full version is in bin/bashbot_init.in.sh
752762
# initialize bot environment, user and permissions
@@ -788,6 +798,8 @@ fi
788798
# source the script with source as param to use functions in other scripts
789799
# do not execute if read from other scripts
790800

801+
BOTADMIN="$(getConfigKey "botadmin")"
802+
791803
if [ -z "${SOURCE}" ]; then
792804
##############
793805
# internal options only for use from bashbot and developers
@@ -829,7 +841,8 @@ if [ -z "${SOURCE}" ]; then
829841
;;
830842
# finally starts the read update loop, internal use only
831843
"startbot" )
832-
_exec_if_function start_bot "$2"
844+
_exec_if_function start_bot "$2" "polling mode"
845+
_exec_if_function get_updates "$2"
833846
debug_checks "end startbot" "$@"
834847
exit
835848
;;
@@ -899,7 +912,7 @@ if [ -z "${SOURCE}" ]; then
899912
# shellcheck disable=SC2086
900913
if kill ${BOTPID}; then
901914
# inform botadmin about stop
902-
send_normal_message "$(getConfigKey "botadmin")" "Bot ${ME} stopped ..." &
915+
send_normal_message "${BOTADMIN}" "Bot ${ME} polling mode stopped ..." &
903916
printf "${GREEN}OK. Bot stopped successfully.${NN}"
904917
else
905918
printf "${RED}An error occurred while stopping bot.${NN}"
@@ -912,7 +925,7 @@ if [ -z "${SOURCE}" ]; then
912925
exit
913926
;;
914927
# suspend, resume or kill background jobs
915-
"suspendb"*|"resumeb"*|"killb"*)
928+
"suspendb"*|"resumeb"*|'restartb'*|"killb"*)
916929
_is_function job_control || { printf "${RED}Module background is not available!${NN}"; exit 3; }
917930
ME="$(getConfigKey "botname")"
918931
job_control "$1"

bin/any_command.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ USAGE='any_command.sh [-h|--help] [--force|--reference] bot_command args ...'
2121
# AUTHOR: KayM (gnadelwartz), [email protected]
2222
# CREATED: 30.01.2021 10:24
2323
#
24-
#### $$VERSION$$ v1.45-dev-7-ga9ed559
24+
#### $$VERSION$$ v1.5-0-g8adca9b
2525
#===============================================================================
2626

2727
####
@@ -68,7 +68,7 @@ fi
6868
# ready, do stuff here -----
6969
COMMAND="$1"
7070
if [ "$2" == "BOTADMIN" ]; then
71-
ARG1="${BOT_ADMIN}"
71+
ARG1="${BOTADMIN}"
7272
else
7373
ARG1="$2"
7474
fi

bin/bashbot_env.inc.sh

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
# AUTHOR: KayM (gnadelwartz), [email protected]
1414
# CREATED: 18.12.2020 12:27
1515
#
16-
#### $$VERSION$$ v1.40-0-gf9dab50
16+
#### $$VERSION$$ v1.5-0-g8adca9b
1717
#===============================================================================
1818

1919
############
2020
# set where your bashbot lives
2121
export BASHBOT_HOME BASHBOT_ETC BASHBOT_VAR FILE_REGEX ME
2222

2323
# default: one dir up
24-
BASHBOT_HOME="$(cd "${BASH_SOURCE[0]%/*}" >/dev/null 2>&1 && pwd)/../"
25-
[ "${BASHBOT_HOME}" = "/../" ] && BASHBOT_HOME="../"
24+
BASHBOT_HOME="$(cd "${BASH_SOURCE[0]%/*}/../" >/dev/null 2>&1 && pwd)"
25+
[ "${BASHBOT_HOME}" = "" ] && BASHBOT_HOME="../"
2626

2727
# set you own BASHBOT_HOME if different, e.g.
2828
# BASHBOT_HOME="/usr/local/telegram-bot-bash"
@@ -59,11 +59,14 @@ UPLOADDIR="${BASHBOT_VAR%/bin*}"
5959
FILE_REGEX="${UPLOADDIR}/.*"
6060

6161
# get and check ADMIN and NAME
62-
BOT_ADMIN="$(getConfigKey "botadmin")"
63-
BOT_NAME="$(getConfigKey "botname")"
64-
ME="${BOT_NAME}"
65-
[[ -z "${BOT_ADMIN}" || "${BOT_ADMIN}" == "?" ]] && printf "%s\n" "${ORANGE}Warning: Botadmin not set, send bot command${NC} /start"
66-
[[ -z "${BOT_NAME}" ]] && printf "%s\n" "${ORANGE}Warning: Botname not set, run bashbot.sh botname"
62+
BOTNAME="$(getConfigKey "botname")"
63+
ME="${BOTNAME}"
64+
[[ -z "${BOTADMIN}" || "${BOTADMIN}" == "?" ]] && printf "%s\n" "${ORANGE}Warning: Botadmin not set, send bot command${NC} /start"
65+
[[ -z "${BOTNAME}" ]] && printf "%s\n" "${ORANGE}Warning: Botname not set, run bashbot.sh botname"
66+
67+
# default webhook pipe
68+
export WEBHOOK="${DATADIR}/webhook-fifo-${ME}"
69+
6770

6871
# output command result or Telegram response
6972
print_result() { jssh_printDB "BOTSENT" | sort -r; }

bin/bashbot_init.inc.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# AUTHOR: KayM (gnadelwartz), [email protected]
1212
# CREATED: 27.01.2021 13:42
1313
#
14-
#### $$VERSION$$ v1.45-dev-3-g429c230
14+
#### $$VERSION$$ v1.5-0-g8adca9b
1515
#===============================================================================
1616
# shellcheck disable=SC2059
1717

@@ -76,7 +76,7 @@ bot_init() {
7676
[ -n "${INTERACTIVE}" ] && read -r runuser
7777
fi
7878
# check if mycommands exist
79-
if [ ! -r "${BASHBOT_ETC:-.}/mycommands.sh" ]; then
79+
if [[ ! -r "${BASHBOT_ETC:-.}/mycommands.sh" && -r ${BASHBOT_ETC:-.}/mycommands.sh.dist ]]; then
8080
printf "Mycommands.sh not found, copy ${GREY}<C>lean file, <E>xamples or <N>one${NC} to mycommands.sh? (c/e/N) N\b"
8181
read -r ANSWER
8282
[[ "${ANSWER}" =~ ^[cC] ]] && cp -f "${BASHBOT_ETC:-.}/mycommands.sh.clean" "${BASHBOT_ETC:-.}/mycommands.sh"
@@ -116,7 +116,7 @@ bot_init() {
116116
if [ -w "bashbot.rc" ]; then
117117
printf "Adjust user and botname in bashbot.rc ...\n"
118118
sed -i '/^[# ]*runas=/ s|runas=.*$|runas="'"${touser}"'"|' "bashbot.rc"
119-
sed -i '/^[# ]*bashbot=/ s|bashbot=.*$|bashbot="cd '"${PWD}"'; '"${PWD}"'/'"${0##*/}"'"|' "bashbot.rc"
119+
sed -i '/^[# ]*bashbotdir=/ s|bashbotdir=.*$|bashbotdir="'"${PWD}"'"|' "bashbot.rc"
120120
botname="$(getConfigKey "botname")"
121121
[ -n "${botname}" ] && sed -i '/^[# ]*name=/ s|name=.*$|name="'"${botname}"'"|' "bashbot.rc"
122122
printf "Done.\n"

0 commit comments

Comments
 (0)