#!/bin/bash # bashbot, the Telegram bot written in bash. # Written by @topkecleon, Juan Potato (@awkward_potato), Lorenzo Santina (BigNerd95) and Daniil Gentili (@danog) # https://github.com/topkecleon/telegram-bot-bash # Depends on ./JSON.sh (http://github.com/dominictarr/./JSON.sh), # which is MIT/Apache-licensed # And on tmux (https://github.com/tmux/tmux), # which is BSD-licensed # This file is public domain in the USA and all free countries. # If you're in Europe, and public domain does not exist, then haha. TOKEN='tokenhere' URL='https://api.telegram.org/bot'$TOKEN # Set INLINE to 1 in order to receive inline queries. # To enable this option in your bot, send the /setinline command to @BotFather. INLINE=0 MSG_URL=$URL'/sendMessage' PHO_URL=$URL'/sendPhoto' AUDIO_URL=$URL'/sendAudio' DOCUMENT_URL=$URL'/sendDocument' STICKER_URL=$URL'/sendSticker' VIDEO_URL=$URL'/sendVideo' VOICE_URL=$URL'/sendVoice' LOCATION_URL=$URL'/sendLocation' ACTION_URL=$URL'/sendChatAction' FORWARD_URL=$URL'/forwardMessage' INLINE_QUERY=$URL'/answerInlineQuery' FILE_URL='https://api.telegram.org/file/bot'$TOKEN'/' UPD_URL=$URL'/getUpdates?offset=' GET_URL=$URL'/getFile' OFFSET=0 declare -A USER MESSAGE URLS CONTACT LOCATION send_message() { local chat="$1" local text="$(echo "$2" | sed 's/ mykeyboardstartshere.*//g;s/ myfilelocationstartshere.*//g;s/ mylatstartshere.*//g;s/ mylongstartshere.*//g')" [ "$3" != "text" ] && { local keyboard="$(echo "$2" | sed '/mykeyboardstartshere /!d;s/.*mykeyboardstartshere //g;s/ myfilelocationstartshere.*//g;s/ mylatstartshere.*//g;s/ mylongstartshere.*//g')" local file="$(echo "$2" | sed '/myfilelocationstartshere /!d;s/.*myfilelocationstartshere //g;s/ mykeyboardstartshere.*//g;s/ mylatstartshere.*//g;s/ mylongstartshere.*//g')" local lat="$(echo "$2" | sed '/mylatstartshere /!d;s/.*mylatstartshere //g;s/ mykeyboardstartshere.*//g;s/ myfilelocationstartshere.*//g;s/ mylongstartshere.*//g')" local long="$(echo "$2" | sed '/mylongstartshere /!d;s/.*mylongstartshere //g;s/ mykeyboardstartshere.*//g;s/ myfilelocationstartshere.*//g;s/ mylatstartshere.*//g')" } if [ "$keyboard" != "" ]; then send_keyboard "$chat" "$text" "$keyboard" local sent=y fi if [ "$file" != "" ]; then send_file "$chat" "$file" "$text" local sent=y fi if [ "$lat" != "" -a "$long" != "" ]; then send_location "$chat" "$lat" "$long" local sent=y fi if [ "$sent" != "y" ];then res=$(curl -s "$MSG_URL" -F "chat_id=$chat" -F "text=$text") fi } send_markdown_message() { res=$(curl -s "$MSG_URL" -F "chat_id=$1" -F "text=$2" -F "parse_mode=markdown") } answer_inline_query() { case $2 in "article") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","title":"'$3'","message_text":"'$4'"}]' ;; "photo") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","photo_url":"'$3'","thumb_url":"'$4'"}]' ;; "gif") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","gif_url":"'$3'"}]' ;; "mpeg4_gif") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","mpeg4_url":"'$3'"}]' ;; "video") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","video_url":"'$3'","mime_type":"'$4'","thumb_url":"'$5'","title":"'$6'"}]' ;; "audio") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","audio_url":"'$3'","title":"'$4'"}]' ;; "voice") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","voice_url":"'$3'","title":"'$4'"}]' ;; "document") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","title":"'$3'","caption":"'$4'","document_url":"'$5'","mime_type":"'$6'"}]' ;; "location") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}]' ;; "venue") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","latitude":"'$3'","longitude":"'$4'","title":"'$5'","address":"'$6'"}]' ;; "contact") InlineQueryResult='[{"type":"'$2'","id":"$RANDOM","phone_number":"'$3'","first_name":"'$4'"}]' ;; # Cached media stored in Telegram server "cached_photo") InlineQueryResult='[{"type":"photo","id":"$RANDOM","photo_file_id":"'$3'"}]' ;; "cached_gif") InlineQueryResult='[{"type":"gif","id":"$RANDOM","gif_file_id":"'$3'"}]' ;; "cached_mpeg4_gif") InlineQueryResult='[{"type":"mpeg4_gif","id":"$RANDOM","mpeg4_file_id":"'$3'"}]' ;; "cached_sticker") InlineQueryResult='[{"type":"sticker","id":"$RANDOM","sticker_file_id":"'$3'"}]' ;; "cached_document") InlineQueryResult='[{"type":"document","id":"$RANDOM","title":"'$3'","document_file_id":"'$4'"}]' ;; "cached_video") InlineQueryResult='[{"type":"video","id":"$RANDOM","video_file_id":"'$3'","title":"'$4'"}]' ;; "cached_voice") InlineQueryResult='[{"type":"voice","id":"$RANDOM","voice_file_id":"'$3'","title":"'$4'"}]' ;; "cached_audio") InlineQueryResult='[{"type":"audio","id":"$RANDOM","audio_file_id":"'$3'"}]' ;; esac res=$(curl -s "$INLINE_QUERY" -F "inline_query_id=$1" -F "results=$InlineQueryResult") } send_keyboard() { local chat="$1" local text="$2" shift 2 local keyboard=init OLDIFS=$IFS IFS=$(echo -en "\"") for f in $*;do [ "$f" != " " ] && local keyboard="$keyboard, [\"$f\"]";done IFS=$OLDIFS local keyboard=${keyboard/init, /} res=$(curl -s "$MSG_URL" --header "content-type: multipart/form-data" -F "chat_id=$chat" -F "text=$text" -F "reply_markup={\"keyboard\": [$keyboard],\"one_time_keyboard\": true}") } get_file() { [ "$1" != "" ] && echo $FILE_URL$(curl -s "$GET_URL" -F "file_id=$1" | ./JSON.sh -s | egrep '\["result","file_path"\]' | cut -f 2 | cut -d '"' -f 2) } send_file() { [ "$2" = "" ] && return local chat_id=$1 local file=$2 echo "$file" | grep -qE '/home/allowed/.*' || return local ext="${file##*.}" case $ext in "mp3") CUR_URL=$AUDIO_URL WHAT=audio STATUS=upload_audio ;; png|jpg|jpeg|gif) CUR_URL=$PHO_URL WHAT=photo STATUS=upload_photo ;; webp) CUR_URL=$STICKER_URL WHAT=sticker STATUS= ;; mp4) CUR_URL=$VIDEO_URL WHAT=video STATUS=upload_video ;; ogg) CUR_URL=$VOICE_URL WHAT=voice STATUS= ;; *) CUR_URL=$DOCUMENT_URL WHAT=document STATUS=upload_document ;; esac send_action $chat_id $STATUS res=$(curl -s "$CUR_URL" -F "chat_id=$chat_id" -F "$WHAT=@$file" -F "caption=$3") } # 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 res=$(curl -s "$ACTION_URL" -F "chat_id=$1" -F "action=$2") } send_location() { [ "$3" = "" ] && return res=$(curl -s "$LOCATION_URL" -F "chat_id=$1" -F "latitude=$2" -F "longitude=$3") } forward() { [ "$3" = "" ] && return res=$(curl -s "$FORWARD_URL" -F "chat_id=$1" -F "from_chat_id=$2" -F "message_id=$3") } startproc() { killproc mkfifo $copname TMUX= tmux new-session -d -s $copname "$* &>$copname; echo >$copname; sleep 5; rm -r $copname" while [ -p "$copname" ];do read -t 10 line [ "$line" != "" ] && send_message "${USER[ID]}" "$line" line= done <$copname } killproc() { (rm -r $copname tmux kill-session -t $copname)2>/dev/null } inproc() { tmux send-keys -t $copname "$MESSAGE ${URLS[*]} " } process_client() { # User USER[FIRST_NAME]=$(echo "$res" | egrep '\["result",0,"message","chat","first_name"\]' | cut -f 2 | cut -d '"' -f 2) USER[LAST_NAME]=$(echo "$res" | egrep '\["result",0,"message","chat","last_name"\]' | cut -f 2 | cut -d '"' -f 2) USER[USERNAME]=$(echo "$res" | sed 's/^.*\(username.*\)/\1/g' | cut -d '"' -f3) # Audio URLS[AUDIO]=$(get_file $(echo "$res" | egrep '\["result",0,"message","audio","file_id"\]' | cut -f 2 | cut -d '"' -f 2)) # Document URLS[DOCUMENT]=$(get_file $(echo "$res" | egrep '\["result",0,"message","document","file_id"\]' | cut -f 2 | cut -d '"' -f 2)) # Photo URLS[PHOTO]=$(get_file $(echo "$res" | egrep '\["result",0,"message","photo",.*,"file_id"\]' | cut -f 2 | cut -d '"' -f 2 | sed -n '$p')) # Sticker URLS[STICKER]=$(get_file $(echo "$res" | egrep '\["result",0,"message","sticker","file_id"\]' | cut -f 2 | cut -d '"' -f 2)) # Video URLS[VIDEO]=$(get_file $(echo "$res" | egrep '\["result",0,"message","video","file_id"\]' | cut -f 2 | cut -d '"' -f 2)) # Voice URLS[VOICE]=$(get_file $(echo "$res" | egrep '\["result",0,"message","voice","file_id"\]' | cut -f 2 | cut -d '"' -f 2)) # Contact CONTACT[NUMBER]=$(echo "$res" | egrep '\["result",0,"message","contact","phone_number"\]' | cut -f 2 | cut -d '"' -f 2) CONTACT[FIRST_NAME]=$(echo "$res" | egrep '\["result",0,"message","contact","first_name"\]' | cut -f 2 | cut -d '"' -f 2) CONTACT[LAST_NAME]=$(echo "$res" | egrep '\["result",0,"message","contact","last_name"\]' | cut -f 2 | cut -d '"' -f 2) CONTACT[USER_ID]=$(echo "$res" | egrep '\["result",0,"message","contact","user_id"\]' | cut -f 2 | cut -d '"' -f 2) # Caption CAPTION=$(echo "$res" | egrep '\["result",0,"message","caption"\]' | cut -f 2 | cut -d '"' -f 2) # Location LOCATION[LONGITUDE]=$(echo "$res" | egrep '\["result",0,"message","location","longitude"\]' | cut -f 2 | cut -d '"' -f 2) LOCATION[LATITUDE]=$(echo "$res" | egrep '\["result",0,"message","location","latitude"\]' | cut -f 2 | cut -d '"' -f 2) NAME="$(basename ${URLS[*]} &>/dev/null)" # Tmux copname="CO${USER[ID]}" if ! tmux ls | grep -q $copname; then [ ! -z ${URLS[*]} ] && { curl -s ${URLS[*]} -o $NAME send_file "${USER[ID]}" "$NAME" "$CAPTION" rm "$NAME" } [ ! -z ${LOCATION[*]} ] && send_location "${USER[ID]}" "${LOCATION[LATITUDE]}" "${LOCATION[LONGITUDE]}" # Inline if [ $INLINE == 1 ]; then # inline query data iUSER[FIRST_NAME]=$(echo "$res" | sed 's/^.*\(first_name.*\)/\1/g' | cut -d '"' -f3 | tail -1) iUSER[LAST_NAME]=$(echo "$res" | sed 's/^.*\(last_name.*\)/\1/g' | cut -d '"' -f3) iUSER[USERNAME]=$(echo "$res" | sed 's/^.*\(username.*\)/\1/g' | cut -d '"' -f3 | tail -1) iQUERY_ID=$(echo "$res" | sed 's/^.*\(inline_query.*\)/\1/g' | cut -d '"' -f5 | tail -1) iQUERY_MSG=$(echo "$res" | sed 's/^.*\(inline_query.*\)/\1/g' | cut -d '"' -f5 | tail -6 | head -1) # Inline examples if [[ $iQUERY_MSG == photo ]]; then answer_inline_query "$iQUERY_ID" "photo" "http://blog.techhysahil.com/wp-content/uploads/2016/01/Bash_Scripting.jpeg" "http://blog.techhysahil.com/wp-content/uploads/2016/01/Bash_Scripting.jpeg" fi if [[ $iQUERY_MSG == sticker ]]; then answer_inline_query "$iQUERY_ID" "cached_sticker" "BQADBAAD_QEAAiSFLwABWSYyiuj-g4AC" fi if [[ $iQUERY_MSG == gif ]]; then answer_inline_query "$iQUERY_ID" "cached_gif" "BQADBAADIwYAAmwsDAABlIia56QGP0YC" fi if [[ $iQUERY_MSG == web ]]; then answer_inline_query "$iQUERY_ID" "article" "Telegram" "https://telegram.org/" fi fi case $MESSAGE in '/question') startproc "./question"& ;; '/info') send_message "${USER[ID]}" "This is bashbot, the Telegram bot written entirely in bash." ;; '/start') send_message "${USER[ID]}" "This is bashbot, the Telegram bot written entirely in bash. Features background tasks and interactive chats. Can serve as an interface for cli programs. Currently can send, recieve and forward messages, custom keyboards, photos, audio, voice, documents, locations and video files. Available commands: /start: Start bot and get this message. /info: Get shorter info message about this bot. /question: Start interactive chat. /cancel: Cancel any currently running interactive chats. Written by @topkecleon, Juan Potato (@awkward_potato), Lorenzo Santina (BigNerd95) and Daniil Gentili (@danogentili) Contribute to the project: https://github.com/topkecleon/telegram-bot-bash " ;; '') ;; *) send_message "${USER[ID]}" "$MESSAGE" "text" esac else case $MESSAGE in '/cancel') killproc send_message "${USER[ID]}" "Command canceled." ;; *) inproc;; esac fi } # source the script with source as param to use functions in other scripts while [ "$1" != "source" ]; do { res=$(curl -s $UPD_URL$OFFSET | ./JSON.sh -s) # Target USER[ID]=$(echo "$res" | egrep '\["result",0,"message","chat","id"\]' | cut -f 2) # Offset OFFSET=$(echo "$res" | egrep '\["result",0,"update_id"\]' | cut -f 2) # Message MESSAGE=$(echo "$res" | egrep '\["result",0,"message","text"\]' | cut -f 2 | cut -d '"' -f 2) OFFSET=$((OFFSET+1)) if [ $OFFSET != 1 ]; then process_client& fi }; done