260 lines
10 KiB
Bash
Executable File
260 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# this is just a library
|
|
# you will need to provide the following in the implementation:
|
|
# 1. the api url with it's corresponding token as
|
|
# api_url="api.telegram.org/bot[insert your api token]/"
|
|
# pay attention to the trailing forward slash
|
|
# 2. the following files to read and write from
|
|
# updates.txt
|
|
# sentmsgs.txt
|
|
# delmsgs.txt
|
|
# 3. (optional) a directory for the bot's files as
|
|
# bot_tmpdir="/some/path/"
|
|
# pay attention to the trailing forward slash
|
|
# IMPORTANT: this library is not asynchronous, only one message
|
|
# is read at a time and is done so in order
|
|
# IMPORTANT: this library uses the getUpdates method with
|
|
# tcp_keepalive,NOT webhooks
|
|
|
|
# attempts to grab the last update's update_id and prints it to stdout
|
|
calc_offset() {
|
|
local current="$(bc -l <<< "$(tail -n 1 "${bot_tmpdir}updates.txt" | \
|
|
grep -om1 "\"update_id\":[0-9]\+" | \
|
|
grep -om1 "[0-9]\+") + 1" 2>/dev/null)"
|
|
[ ! -z "${current}" ] && printf "%d" "${current}"
|
|
}
|
|
|
|
# captures output of calc_offset to use as offset and fetches a single update with that offset
|
|
# uses tcp_keepalive method for 300 seconds, but telegram only keeps the connection open for 60 seconds
|
|
# prints result of fetching update if not empty and formats it for better use with the other functions
|
|
getupd() {
|
|
local offset="$(calc_offset)"
|
|
local update="$(curl -sX GET --keepalive-time 300 "${api_url}getUpdates" -d "offset=${offset}" \
|
|
-d "limit=1" -d "timeout=300")"
|
|
update="$(sed 's/{"ok":true,"result":\[\]}\|^{"ok":false.*\|^{"ok":true,"result":\[{\?//g;:a;N;$!ba;s/\n//g;s/}\]}$//g;' <<< "${update}")"
|
|
[ ! -z "${update}" ] && printf "%s\n" "${update}" >> "${bot_tmpdir}updates.txt"
|
|
}
|
|
|
|
# when another comment refers to "getX functions", the following are what is being refered to
|
|
# $1 is the filename of a file with telegram JSON responses to the getupd function
|
|
# if ${bot_tmpdir} is set, it will be appended before the filename
|
|
getmsg_id() {
|
|
tail -n1 "${bot_tmpdir}${1}" | grep -om1\
|
|
"\"message\":{\"message_id\":[0-9]\+\|^\"message_id\":[0-9]\+" | grep -om1 "[0-9]\+"
|
|
}
|
|
|
|
getusr_id() {
|
|
tail -n1 "${bot_tmpdir}${1}" | grep -om1\
|
|
"\"from\":{\"id\":[0-9]\+" | grep -om1 "[0-9]\+"
|
|
}
|
|
|
|
getchat_id() {
|
|
tail -n1 "${bot_tmpdir}${1}" | grep -om1\
|
|
"\"chat\":{\"id\":-\?[0-9]\+" | grep -om1 "\-\?[0-9]\+"
|
|
}
|
|
|
|
# makes decoding emojis possible, important for sending them too
|
|
utf-16-surrogate-pair-decode() {
|
|
# shamelessly stolen from stackoverflow
|
|
local out="$1"
|
|
local remain=""
|
|
local regexp='(.*)\\u[dD]([0-9a-fA-F]{3})\\u[dD]([0-9a-fA-F]{3})(.*)'
|
|
while [[ "${out}" =~ $regexp ]] ; do
|
|
# match 2 \udxxx hex values, calculate new U, then split and replace
|
|
local W1="$(( ( 0xd${BASH_REMATCH[2]} & 0x3ff) <<10 ))"
|
|
local W2="$(( 0xd${BASH_REMATCH[3]} & 0x3ff ))"
|
|
U="$(( ( W1 | W2 ) + 0x10000 ))"
|
|
remain="$(printf '\\U%8.8x' "${U}")${BASH_REMATCH[4]}${remain}"
|
|
out="${BASH_REMATCH[1]}"
|
|
done
|
|
echo -e "${out}${remain}"
|
|
}
|
|
|
|
gettext() {
|
|
local text="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'{\?\("text"\|"caption"\):"\(\\"\|[^"]*\)*"\(,\|}\)' | grep -v "^{" | tail -n1)"
|
|
grep "^\"text\"" <<< "${text}" >/dev/null && utf-16-surrogate-pair-decode "${text:8:-2}"\
|
|
|| {
|
|
grep "^\"caption\"" <<< "${text}" >/dev/null && utf-16-surrogate-pair-decode "${text:11:-2}"
|
|
}
|
|
}
|
|
|
|
getusr_name(){
|
|
local user_name="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'"first_name":"\(\\"\|[^"]*\)*",\("username"\|"language_code"\)' | head -n1)"
|
|
grep "\"username\"$" <<< "${user_name}" >/dev/null && utf-16-surrogate-pair-decode "${user_name:14:-12}"\
|
|
|| {
|
|
grep "\"language_code\"$" <<< "${user_name}" >/dev/null && utf-16-surrogate-pair-decode "${user_name:14:-17}"
|
|
}
|
|
}
|
|
|
|
getusrname(){
|
|
local username="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'"username":"\(\\"\|[^"]*\)*"\(,"language_code"\|},"chat"\)' | head -n1)"
|
|
grep "\"language_code\"$" <<< "${username}" >/dev/null && utf-16-surrogate-pair-decode "${username:12:-17}"\
|
|
|| {
|
|
grep "},\"chat\"$" <<< "${username}" >/dev/null && utf-16-surrogate-pair-decode "${username:12:-9}"
|
|
}
|
|
}
|
|
|
|
getchat_title(){
|
|
local chat_title="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'"title":"\(\\"\|[^"]*\)*","type":"group"' | head -n1)"
|
|
[ ! -z "${chat_title}" ] && utf-16-surrogate-pair-decode "${chat_title:9:-16}"\
|
|
|| getusr_name "${1}"
|
|
}
|
|
|
|
getcbk_data() {
|
|
local callbacks="$(tail -n 1 "${bot_tmpdir}${1}"\
|
|
| grep "\"callback_query\"" | grep -om1 '"data":"\(\\"\|[^"]*\)*"}')"
|
|
[ ! -z "${callbacks}" ] && utf-16-surrogate-pair-decode "${callbacks:8:-2}"
|
|
}
|
|
|
|
gettimestamp() {
|
|
local timestamp="$(tail -n 1 "${bot_tmpdir}${1}"\
|
|
| grep "\"date\"" | grep -om1 "\"date\":[0-9]\+," | head -n1)"
|
|
[ ! -z "${timestamp}" ] && date "+%D %H:%M" -d "@${timestamp:7:-1}"
|
|
}
|
|
|
|
getreplymsg_id(){
|
|
local replymsg_id="$(tail -n 1 "${bot_tmpdir}${1}"\
|
|
| grep -om1 "\"reply_to_message\":{\"message_id\":[0-9]\+" | grep -o "[0-9]\+")"
|
|
[ ! -z "${replymsg_id}" ] && utf-16-surrogate-pair-decode "${replymsg_id}"
|
|
}
|
|
|
|
getreplyusr_name(){
|
|
local user_name="$(tail -n 1 "${bot_tmpdir}${1}"\
|
|
| grep -om1 '"first_name":"\(\\"\|[^"]*\)*","username"' | tail -n2 | head -n1)"
|
|
grep "\"username\"$" <<< "${user_name}" >/dev/null\
|
|
&& utf-16-surrogate-pair-decode "${user_name:14:-12}"
|
|
}
|
|
|
|
getreplyusrname(){
|
|
local username="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'"username":"\(\\"\|[^"]*\)*"\(,"language_code"\|},"chat"\)' | tail -n1)"
|
|
grep "\"language_code\"$" <<< "${username}" >/dev/null && utf-16-surrogate-pair-decode "${username:12:-17}"\
|
|
|| {
|
|
grep "},\"chat\"$" <<< "${username}" >/dev/null && utf-16-surrogate-pair-decode "${username:12:-9}"
|
|
}
|
|
}
|
|
|
|
getreplytext() {
|
|
grep "reply_to_message" "${bot_tmpdir}${1}" >/dev/null && local replytext="$(tail -n 1 "${bot_tmpdir}${1}" | grep -om1\
|
|
'{\?\("text"\|"caption"\):"\(\\"\|[^"]*\)*"\(,\|}\)' | grep -v "^{" | head -n1)"
|
|
grep "^\"text\"" <<< "${replytext}" >/dev/null && utf-16-surrogate-pair-decode "${replytext:8:-2}"\
|
|
|| {
|
|
grep "^\"caption\"" <<< "${replytext}" >/dev/null && utf-16-surrogate-pair-decode "${replytext:11:-2}"
|
|
}
|
|
}
|
|
|
|
# $1 is name of a bash array with the number of columns in every row such as ( 1 1 3 ) for a 3 row keyboard in which the first and second row have 1 column, and the third has 3
|
|
# $2 is name of a bash array with as many text entries as there are columns, they are added left to right, up to down, to the rows/columns configuration passed as first arg
|
|
# $3 is name of a bash array with as many data entries as there are columns, they are added left to right, up to down, to the rows/columns configuration passed as first arg
|
|
mkinline_kbd(){
|
|
local -n rows="${1}"
|
|
local -n text="${2}"
|
|
local -n data="${3}"
|
|
local kbd="{\"inline_keyboard\":[]}"
|
|
local unit="\"text\":\"\",\"callback_data\":\"\""
|
|
local rowformat="[]"
|
|
local colformat="{}"
|
|
local rowfirstpass=1
|
|
local colfirstpass=1
|
|
|
|
for (( i=0; i<"${#rows[@]}"; i++ )); do
|
|
[ "${rowfirstpass}" -eq 1 ] && {
|
|
kbd="$(sed "s/]}$/${rowformat}]}/" <<< "${kbd}")"
|
|
rowfirstpass=0
|
|
} || kbd="$(sed "s/]}$/,${rowformat}]}/" <<< "${kbd}")"
|
|
for (( j=0; j<"${rows[${i}]}"; j++)); do
|
|
[ "${colfirstpass}" -eq 1 ] && {
|
|
kbd="$(sed "s/]]}$/${colformat}]]}/" <<< "${kbd}")"
|
|
colfirstpass=0
|
|
} || kbd="$(sed "s/]]}$/,${colformat}]]}/" <<< "${kbd}")"
|
|
kbd="$(sed "s/}]]}$/${unit}}]]}/" <<< "${kbd}")"
|
|
done
|
|
colfirstpass=1
|
|
done
|
|
|
|
for i in "${text[@]}"; do
|
|
kbd="$(sed "s/\"text\":\"\"/\"text\":\"${i}\"/" <<< "${kbd}")"
|
|
done
|
|
|
|
for i in "${data[@]}"; do
|
|
kbd="$(sed "s/\"callback_data\":\"\"/\"callback_data\":\"${i}\"/" <<< "${kbd}")"
|
|
done
|
|
sed "s/ /+/g;:a;N;$!ba;s/\n/%0A/g" <<< "${kbd}"
|
|
}
|
|
|
|
# $1 is target chat_id
|
|
# $2 is text
|
|
# $3 is inline keyboard (optional)
|
|
sendmsg() {
|
|
local sentmsg
|
|
[ -z "${3}" ] && sentmsg="$(curl -sX GET "${api_url}sendMessage" -d "chat_id=${1}" \
|
|
-d "text=${2}")" \
|
|
|| sentmsg="$(curl -sX GET "${api_url}sendMessage" -d "chat_id=${1}" \
|
|
-d "text=${2}" \
|
|
-d "reply_markup=${3}")"
|
|
sentmsg="$(sed 's/^{"ok":false.*\|^{"ok":true,"result":{\|}$//g' <<< "${sentmsg}")"
|
|
[ ! -z "${sentmsg}" ] && printf "%s\n" "${sentmsg}" >> "${bot_tmpdir}sentmsgs.txt"
|
|
}
|
|
|
|
# $1 is target chat_id
|
|
# $2 is target msg_id
|
|
# $3 is text
|
|
# $4 is inline keyboard (optional)
|
|
replymsg() {
|
|
local sentmsg
|
|
[ -z "${4}" ] &&\
|
|
sentmsg="$(curl -sX GET "${api_url}sendMessage" -d "chat_id=${1}" -d "reply_to_message_id=${2}"\
|
|
-d "text=${3}")" ||
|
|
sentmsg="$(curl -sX GET "${api_url}sendMessage" -d "chat_id=${1}" -d "reply_to_message_id=${2}"\
|
|
-d "text=${3}" -d "reply_markup=${4}")"
|
|
sentmsg="$(sed 's/^{"ok":false.*\|^{"ok":true,"result":{\|}$//g' <<< "${sentmsg}")"
|
|
[ ! -z "${sentmsg}" ] && printf "%s\n" "${sentmsg}" >> "${bot_tmpdir}sentmsgs.txt"
|
|
}
|
|
|
|
# $1 is target chat_id
|
|
# $2 is target msg_id
|
|
# $3 is text
|
|
# $4 is inline keyboard (optional)
|
|
editmsg() {
|
|
local editmsg
|
|
[ -z "${4}" ] && editmsg="$(curl -sX GET "${api_url}editMessageText" -d "chat_id=${1}" -d "message_id=${2}" \
|
|
-d "text=${3}")" || editmsg="$(curl -sX GET "${api_url}editMessageText" -d "chat_id=${1}" -d "message_id=${2}" \
|
|
-d "text=${3}" -d "reply_markup=${4}")"
|
|
editmsg="$(sed 's/^{"ok":false.*\|^{"ok":true,"result":{\|}$//g' <<< "${editmsg}")"
|
|
[ ! -z "${editmsg}" ] && printf "%s\n" "${editmsg}" >> "${bot_tmpdir}sentmsgs.txt"
|
|
}
|
|
|
|
# $1 is target chat_id
|
|
# $2 is target msg_id
|
|
delmsg() {
|
|
local delmsg
|
|
delmsg="$(curl -sX GET "${api_url}deleteMessage" -d "chat_id=${1}"\
|
|
-d "message_id=${2}")"
|
|
[ ! -z "${delmsg}" ] && printf "deleted message %s from chat %s\n" "${2}" "${1}" >> "${bot_tmpdir}delmsgs.txt"
|
|
}
|
|
|
|
# $1 is the name of a bash associative array to be filled with the message contents
|
|
# $2 is the filename to be passed to the getX functions
|
|
getmsg_content(){
|
|
local -n _REF="${1}"
|
|
_REF[user_id]="$(getusr_id "${2}")"
|
|
_REF[user_name]="$(getusr_name "${2}")"
|
|
_REF[username]="$(getusrname "${2}")"
|
|
_REF[chat_id]="$(getchat_id "${2}")"
|
|
_REF[chat_title]="$(getchat_title "${2}")"
|
|
_REF[msg_id]="$(getmsg_id "${2}")"
|
|
_REF[text]="$(gettext "${2}")"
|
|
_REF[callback]="$(getcbk_data "${2}")"
|
|
_REF[timestamp]="$(gettimestamp "${2}")"
|
|
_REF[replytext]="$(getreplytext "${2}")"
|
|
_REF[replymsg_id]="$(getreplymsg_id "${2}")"
|
|
_REF[replyusername]="$(getreplyusrname "${2}")"
|
|
_REF[replyuser_name]="$(getreplyusr_name "${2}")"
|
|
|
|
}
|