├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── bashbot ├── builder └── builder ├── docker-compose.yml ├── dockerfiles ├── bashbot.Dockerfile └── builder.Dockerfile └── first_start /.gitignore: -------------------------------------------------------------------------------- 1 | database/ 2 | tokens 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: required 3 | dist: trusty 4 | 5 | install: 6 | - > 7 | curl -sSL "https://get.docker.com/gpg" | 8 | sudo -E apt-key add - 9 | - > 10 | echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | 11 | sudo tee -a /etc/apt/sources.list 12 | - sudo apt-get update 13 | - > 14 | sudo apt-get -o Dpkg::Options::="--force-confdef" \ 15 | -o Dpkg::Options::="--force-confold" --assume-yes install --allow-unauthenticated docker-engine 16 | - docker version 17 | - sudo pip install docker-compose 18 | - docker-compose version 19 | - ./first_start 20 | 21 | script: 22 | - docker-compose up --build -d 23 | 24 | after_script: 25 | - docker-compose down 26 | 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BashBot 2 | 3 | This is my first telegram bot written in bash 4 | 5 | ## Commands 6 | 7 | ``` 8 | /weather - Weather in a city for now, today and tommorow 9 | /w - Short form of /w 10 | /info - Information about this bot 11 | /wset - Set default city for /weather 12 | /ibash - Random quote from ibash.org.ru 13 | /loglist - Random quote from loglist.net 14 | /roll - Rolling a random number from 0 to 99 15 | /google - Send a link to google.com/String 16 | /md5 - Creating a md5 sum of String 17 | /manage - Manage your database (Only for admins) 18 | ``` 19 | 20 | ## Starting 21 | 22 | ``` 23 | git clone https://github.com/Cuttlerat/bashbot.git 24 | cd bashbot 25 | ``` 26 | 27 | Run `first_start` for a database and token file creation 28 | 29 | ``` 30 | ./first_start 31 | ``` 32 | 33 | Add your telegram username and tokens into `./tokens` file 34 | ``` 35 | # Tokens should be specified without any quotes 36 | BOT_TOKEN= 37 | WEATHER_TOKEN= 38 | ADMINS=( "YOUR TELEGRAM USERNAME WITHOUT @" ) 39 | ``` 40 | 41 | Then just launch the bot 42 | 43 | ``` 44 | ./bashbot 45 | ``` 46 | 47 | ### Starting in a docker container 48 | 49 | In order to build and create a docker container, `docker-compose` must be installed in your system 50 | 51 | ``` 52 | docker-compose up --build bashbot 53 | ``` 54 | 55 | Also you can take my container from Dockerhub 56 | 57 | ``` 58 | docker run -v ./database:/database cuttlerat/bashbot 59 | ``` 60 | 61 | You can get a weather token here: https://www.worldweatheronline.com/
62 | Register your bot here: https://t.me/BotFather 63 | 64 | ## How to /manage database 65 | 66 | If you want that your bot triggers on a some type of messages, there some options here. 67 | 68 | ### Google 69 | 70 | If you want that your bot triggers on a messages like this 71 | ``` 72 | Cuttlerat: What is Jenkins? 73 | Cuttlebot: https://www.google.ru/search?q=Jenkins 74 | ``` 75 | 76 | You will need to add a note in your database like this: 77 | 78 | ``` 79 | /manage INSERT INTO google(match) VALUES("what is") 80 | ``` 81 | 82 | All matches must be in a low case! It's important (I will do something with it later) 83 | 84 | If you don't want any word to trigger this function 85 | 86 | ``` 87 | /manage INSERT INTO google_ignore(ignore) VALUES("Jenkins") 88 | Cuttlerat: What is Jenkins? 89 | *no answer* 90 | ``` 91 | 92 | ### Simple trigger 93 | 94 | If you want a simple trigger on a message responding with your specified string 95 | 96 | ``` 97 | /manage INSERT INTO answers(match,string) VALUES("hello!", "Hi!") 98 | Cuttlerat: Hello! 99 | Cutltebot: Hi! 100 | ``` 101 | 102 | ### Ping 103 | 104 | If you want to summon someone with just mentioning of his name or nickname 105 | 106 | ``` 107 | /manage INSERT INTO pingers(username,match) VALUES("Cuttlerat", "rat") 108 | Cuttlerat: rat! 109 | *nothing* 110 | ``` 111 | 112 | Why? Because we didn't set a ping phrase yet 113 | 114 | ``` 115 | /manage INSERT INTO ping_phrases(phrase) VALUES("ping") 116 | Cuttlerat: ping rat 117 | Cuttlebot: @Cuttlerat 118 | ``` 119 | 120 | There is a little trick to summon all persons from the pingers table 121 | 122 | ``` 123 | /manage INSERT INTO pingers(username,match) VALUES("hotkosc", "kosc") 124 | /manage INSERT INTO pingers(username,match) VALUES("EVERYONE GET IN HERE", "all") 125 | Cuttlerat: ping all 126 | Cuttlebot: @Cuttlerat @hotkosc 127 | ``` 128 | 129 | But what if you want to call everyone except one guy? 130 | 131 | You will need to add a ping exclude phrase 132 | 133 | ``` 134 | /manage INSERT INTO ping_exclude(match) VALUES("excpet") 135 | Cuttlerat: ping all except kosc 136 | Cuttlebot: @Cuttlerat 137 | ``` 138 | 139 | And is it! Enjoy your bot! 140 | -------------------------------------------------------------------------------- /bashbot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #=============================================================================== 3 | # 4 | # DESCRIPTION: Telegram bot in bash 5 | # AUTHOR: Kioller Alexey 6 | # E-MAIL: avkioller@gmail.com 7 | # GITHUB: https://github.com/Cuttlerat/bashbot 8 | # CREATED: 07.07.2017 9 | # 10 | #=============================================================================== 11 | 12 | #=== CHECKING TOKENS FILE ==================================================== 13 | 14 | [[ -f ./tokens ]] && . ./tokens 15 | 16 | #=== CONSTANTS =============================================================== 17 | 18 | : "${BOT_TOKEN:="$1"}" 19 | : "${WEATHER_TOKEN:="$2"}" 20 | readonly BOT_TOKEN 21 | readonly WEATHER_TOKEN 22 | readonly ADMINS 23 | readonly DATABASE="./database/database.db" 24 | readonly TIMEOUT="10" 25 | readonly URL="https://api.telegram.org/bot${BOT_TOKEN}" 26 | readonly BOTNAME="@$( curl -s "${URL}/getMe" | \ 27 | jq .result.username | tr -d '"' )" 28 | 29 | #=== FUNCTIONS =============================================================== 30 | 31 | #=== FUNCTION ================================================================ 32 | # NAME: _usage 33 | # DESCRIPTION: Display usage information. 34 | #=============================================================================== 35 | 36 | function _usage () 37 | { 38 | cat <<- EOF 39 | 40 | Usage : ${0##/*/} [options] [--] "" "" 41 | 42 | Github: https://github.com/Cuttlerat/bashbot 43 | 44 | Options: 45 | -h|help Display this message 46 | 47 | EOF 48 | 49 | } # ---------- end of function _usage ---------- 50 | 51 | #=== FUNCTION ================================================================ 52 | # NAME: _send_text 53 | # DESCRIPTION: Sending request to telegram api 54 | #=============================================================================== 55 | function _send_text { 56 | 57 | local TEXT=$( sed 's/^\s\+//g' <<< "${1}" ) 58 | curl -s "${URL}/sendMessage" \ 59 | --max-time "${TIMEOUT}" \ 60 | --output /dev/null \ 61 | --data-urlencode "text=${TEXT}" \ 62 | --data "chat_id=${CHAT_ID}" \ 63 | --data "disable_web_page_preview=${PREVIEW}" \ 64 | --data "parse_mode=${PARSE_MODE}" 65 | 66 | } # ---------- end of function _send_text ---------- 67 | 68 | #=== FUNCTION ================================================================ 69 | # NAME: _weather_request 70 | # DESCRIPTION: Sending request to https://worldweatheronline.com/ api 71 | #=============================================================================== 72 | function _weather_request { 73 | 74 | curl -s "https://api.worldweatheronline.com/premium/v1/weather.ashx" \ 75 | --data-urlencode "q=${ARGS:="$(_get_default_city)"}" \ 76 | --data "key=${WEATHER_TOKEN}" \ 77 | --data "format=json" \ 78 | --data "num_of_days=2" \ 79 | --data "fx24=yes" \ 80 | --data "lang=ru" 81 | 82 | } # ---------- end of function _weather_request ---------- 83 | 84 | #=== FUNCTION ================================================================ 85 | # NAME: _get_default_city 86 | # DESCRIPTION: Get the default city from the database 87 | #=============================================================================== 88 | function _get_default_city { 89 | 90 | sqlite3 "${DATABASE}" "SELECT city 91 | FROM locations 92 | WHERE username=\"default_city\"" 93 | 94 | } # ---------- end of function _get_default_city ---------- 95 | 96 | #=== FUNCTION ================================================================ 97 | # NAME: _get_emoji 98 | # DESCRIPTION: Get the emoji for weather 99 | # PARAMETERS: $1 - Weather code 100 | #=============================================================================== 101 | function _get_emoji { 102 | 103 | read WEATHER_CODE 104 | case $WEATHER_CODE in 105 | 106 | 113) 107 | echo "☀️" 108 | ;; # Sunny 109 | 116) 110 | echo "🌥" 111 | ;; # Partly cloudy 112 | 119) 113 | echo "⛅️" 114 | ;; # Cloudy 115 | 122) 116 | echo "☁️" 117 | ;; # Overcast 118 | 143|248|260) 119 | echo "🌫" 120 | ;; # Fog 121 | 176|263|266|281|293|296|299|\ 122 | 302|311|317|362) 123 | echo "🌧" 124 | ;; # Light rain 125 | 179|182|185|227|230|323|326|\ 126 | 329|332|335|338|350|353|368|\ 127 | 371|374|377) 128 | echo "🌨" 129 | ;; # Snow 130 | 200) 131 | echo "🌩" 132 | ;; # Thunder 133 | 284|305|308|314|320|356|359|365) 134 | echo "🌧" 135 | ;; # Heavy rain 136 | 386|389|392|395) 137 | echo "⛈" 138 | ;; # Rain with thunder 139 | 140 | esac 141 | 142 | } # ---------- end of function _get_emoji ---------- 143 | 144 | #=== FUNCTION ================================================================ 145 | # NAME: _ibash 146 | # DESCRIPTION: Getting random quote from http://ibash.org.ru 147 | #=============================================================================== 148 | function _ibash { 149 | 150 | TEXT="$( curl -s http://ibash.org.ru/random.php |\ 151 | w3m -dump -cols 300 -T 'text/html' |\ 152 | awk '/#[0-9]+/{f=1}/━+/{f=0}f' |\ 153 | sed '1s/ +.*//' )" 154 | _send_text "${TEXT}" 1>&2 155 | 156 | } # ---------- end of function _ibash ---------- 157 | 158 | #=== FUNCTION ================================================================ 159 | # NAME: _loglist 160 | # DESCRIPTION: Getting random quote from http://loglist.net 161 | #=============================================================================== 162 | function _loglist { 163 | 164 | TEXT="$( curl -sL https://loglist.net/api/quote/random |\ 165 | jq -r .id,.content |\ 166 | sed '1s/^/#/' )" 167 | _send_text "${TEXT}" 1>&2 168 | 169 | } # ---------- end of function _loglist ---------- 170 | 171 | #=== FUNCTION ================================================================ 172 | # NAME: _qcycle 173 | # DESCRIPTION: Cycle for a quotes functions 174 | #=============================================================================== 175 | function _qcycle { 176 | 177 | QCOUNT="$( sed -r 's/^[^1-5]$/5/; 178 | s/^[0-9]{2,}$/5/; 179 | s/.*[^0-9].*/1/' <<< "${ARGS}" )" 180 | [[ ${QCOUNT:=1} ]] 181 | for ((i=0;i&2 195 | 196 | } # ---------- end of function _log_echo ---------- 197 | 198 | #=== FUNCTION ================================================================ 199 | # NAME: _offset 200 | # DESCRIPTION: Offset messages 201 | #=============================================================================== 202 | function _offset { 203 | 204 | curl -s "${URL}/getUpdates" \ 205 | --data "offset=$((UPDATE_ID + 1))" \ 206 | -o /dev/null 207 | 208 | } # ---------- end of function _offset ---------- 209 | 210 | #=== END OF FUNCTIONS ======================================================== 211 | 212 | #----------------------------------------------------------------------- 213 | # Handle command line arguments 214 | #----------------------------------------------------------------------- 215 | 216 | if [[ -z ${BOT_TOKEN} ]]; then _usage; exit 1; fi 217 | 218 | while getopts "h" opt 219 | do 220 | case $opt in 221 | 222 | h|help ) _usage; exit 0 ;; 223 | 224 | ? ) _usage; exit 1 ;; 225 | 226 | esac 227 | done 228 | shift $((OPTIND-1)) 229 | 230 | #=== DATABASE CHECKING ======================================================= 231 | 232 | if [[ ! -w "${DATABASE}" ]]; then 233 | touch "${DATABASE}" 234 | chmod 600 "${DATABASE}" 235 | fi 236 | { 237 | sqlite3 "${DATABASE}" "CREATE TABLE answers( 238 | match varchar(255) PRIMARY KEY, 239 | string varchar(255));" 240 | 241 | sqlite3 "${DATABASE}" "CREATE TABLE google( 242 | match varchar(255) PRIMARY KEY);" 243 | 244 | sqlite3 "${DATABASE}" "CREATE TABLE google_ignore( 245 | ignore varchar(255) PRIMARY KEY);" 246 | 247 | sqlite3 "${DATABASE}" "CREATE TABLE locations( 248 | username varchar(255) PRIMARY KEY, 249 | city varchar(255));" 250 | 251 | sqlite3 "${DATABASE}" "CREATE TABLE ping_phrases( 252 | phrase varchar(255) PRIMARY KEY);" 253 | 254 | sqlite3 "${DATABASE}" "CREATE TABLE ping_exclude( 255 | match varchar(255) PRIMARY KEY);" 256 | 257 | sqlite3 "${DATABASE}" "CREATE TABLE pingers( 258 | username varchar(255), 259 | match varchar(255) PRIMARY KEY);" 260 | 261 | } 2>/dev/null 262 | 263 | #=== START =================================================================== 264 | 265 | _log_echo "Started" 266 | 267 | while sleep 1; do 268 | 269 | PARSE_MODE="none" 270 | RAW_MESSAGE=$( curl -s "${URL}/getUpdates" ) 271 | MSG_COUNT=$( jq '.result | length' <<< "${RAW_MESSAGE}" ) 272 | 273 | for i in $( seq 1 "${MSG_COUNT:=0}" ); do 274 | 275 | RESULT_COUNTER=$(( i-1 )) 276 | 277 | UPDATE_ID=$( jq .result[${RESULT_COUNTER}].update_id <<< "${RAW_MESSAGE}" ) 278 | CHAT_ID=$( jq .result[${RESULT_COUNTER}].message.chat.id <<< "${RAW_MESSAGE}" ) 279 | 280 | #------------------------------------------------------------------------------- 281 | # Checking that the command was sent to your bot 282 | #------------------------------------------------------------------------------- 283 | PRIVATE=$( jq -r .result[${RESULT_COUNTER}].message.chat.type <<< "${RAW_MESSAGE}" | sed 's/private/true/' ) 284 | CMD_CHECK=$( jq -r .result[${RESULT_COUNTER}].message.text <<< "${RAW_MESSAGE}" ) 285 | if [[ "${CMD_CHECK}" =~ ${BOTNAME/$/\\b} ]]; then 286 | MESSAGE=$( sed -r "s/${BOTNAME}//" <<< "${CMD_CHECK}" ) 287 | else 288 | MESSAGE="${CMD_CHECK}" 289 | fi 290 | if [[ ! "${MESSAGE}" =~ ^/manage ]]; then # ---------- start of manage check if ---------- 291 | MESSAGE=$( sed "s/\"/\\\"/g;s/'/\\'/g" <<< "${MESSAGE}" | xargs 2>/dev/null ) 292 | R_MESSAGE=$( sed 's/ё/е/g' <<< "${MESSAGE,,}" ) 293 | 294 | #------------------------------------------------------------------------------- 295 | # Google command parser 296 | #------------------------------------------------------------------------------- 297 | if [[ $( sqlite3 "${DATABASE}" \ 298 | "SELECT EXISTS( 299 | SELECT 1 FROM google_ignore 300 | WHERE \"${MESSAGE,,}\" 301 | LIKE '%'||google_ignore.ignore||'%' 302 | ) LIMIT 1;" ) -eq 0 ]] \ 303 | && \ 304 | [[ $( sqlite3 "${DATABASE}" \ 305 | "SELECT EXISTS( 306 | SELECT 1 FROM google 307 | WHERE \"${MESSAGE,,}\" 308 | LIKE '%'||google.match||'%' 309 | ) LIMIT 1;" ) -eq 1 ]]; then 310 | 311 | MATCH=$( sqlite3 "${DATABASE}" \ 312 | "SELECT * FROM google 313 | WHERE \"${MESSAGE,,}\" 314 | LIKE '%'||google.match||'%';" |\ 315 | awk '{printf length; printf " "; print $0}' |\ 316 | sort -rn | awk '{$1=""; print $0}' | head -1 | xargs ) 317 | 318 | MESSAGE=$( sed -r "s#${MATCH}(\s*\S+)#/google\1#i" <<< "${MESSAGE}" ) 319 | fi 320 | 321 | #------------------------------------------------------------------------------- 322 | # Rick and Morty easter egg 323 | #------------------------------------------------------------------------------- 324 | MESSAGE=$( sed -r "s/^SHOW ME WHAT YOU GOT/\/show/" <<< "${MESSAGE}" ) 325 | 326 | #------------------------------------------------------------------------------- 327 | # Weather token check 328 | #------------------------------------------------------------------------------- 329 | RE="^/w\\b|^/weather\\b" 330 | [[ ${MESSAGE} =~ ${RE} ]] && [[ -z "${WEATHER_TOKEN}" ]] && MESSAGE="/none${MESSAGE}" 331 | 332 | #------------------------------------------------------------------------------- 333 | # Parsing pingers in message if message doesn't start with command 334 | #------------------------------------------------------------------------------- 335 | if [[ ! "${MESSAGE}" =~ ^/[a-zA-Z0-9]+ ]] && [[ $( sqlite3 "${DATABASE}" \ 336 | "SELECT EXISTS( 337 | SELECT 1 FROM ping_phrases 338 | WHERE \"${R_MESSAGE}\" 339 | LIKE '%'||ping_phrases.phrase||'%' 340 | ) LIMIT 1;" )\ 341 | -eq 1 ]]; then 342 | MESSAGE=$( sed -r "s/^/\/ping &/" <<< "${R_MESSAGE//ё/е}" ) 343 | fi 344 | 345 | #------------------------------------------------------------------------------- 346 | # Auto answer 347 | #------------------------------------------------------------------------------- 348 | if [[ ! "${MESSAGE}" =~ ^/[a-zA-Z0-9]+ ]] && [[ $( sqlite3 "${DATABASE}" \ 349 | "SELECT EXISTS( 350 | SELECT 1 FROM answers 351 | WHERE \"${R_MESSAGE}\" 352 | LIKE '%'||answers.match||'%' 353 | ) LIMIT 1" )\ 354 | -eq 1 ]]; then 355 | MESSAGE=$( sed -r "s/^/\/answer &/" <<< "${R_MESSAGE}" ) 356 | fi 357 | 358 | fi # ---------- end of manage check if ---------- 359 | 360 | ARGS=$( awk '{$1="";print $0}' <<< "${MESSAGE}" | xargs 2>/dev/null ) 361 | COMMAND=$( awk '{print $1}' <<< "${MESSAGE}" ) 362 | SENDER=$( jq -r .result[${RESULT_COUNTER}].message.from.username <<< "${RAW_MESSAGE}" ) 363 | 364 | #=== COMMANDS ================================================================ 365 | 366 | case "${COMMAND}" in 367 | 368 | #=== COMMAND ================================================================= 369 | # NAME: /weather or /w 370 | # DESCRIPTION: Weather in the city for now, today and tommorow 371 | # PARAMETERS: City 372 | #=============================================================================== 373 | /w|/weather) 374 | 375 | WEATHER_API_CRUTCH=false 376 | CITY="" 377 | PARSE_MODE="markdown" 378 | 379 | while [ "${CITY}" == "" -o "${CITY}" == "null" ]; do 380 | [[ -z ${ARGS} ]] && [[ $(sqlite3 "${DATABASE}" \ 381 | "SELECT EXISTS( 382 | SELECT 1 FROM locations 383 | WHERE \"${SENDER}\" 384 | LIKE locations.username 385 | ) LIMIT 1;" \ 386 | ) -eq 1 ]] && \ 387 | ARGS=$( sqlite3 "${DATABASE}" "SELECT city 388 | FROM locations 389 | WHERE \"${SENDER}\" 390 | LIKE locations.username;" ) 391 | 392 | RAW_WEATHER=$( _weather_request ) 393 | CITY=$( jq -r .data.request[0].query <<< "${RAW_WEATHER}" ) 394 | 395 | [ "${CITY}" == "" -o "${CITY}" == "null" ] && [[ "${WEATHER_API_CRUTCH}" == "true" ]] && break 396 | [ "${CITY}" == "" -o "${CITY}" == "null" ] && WEATHER_API_CRUTCH=true 397 | 398 | done 399 | 400 | TEMP=$( jq -r .data.current_condition[0].temp_C <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 401 | COMMENT=$( jq -r .data.current_condition[0].lang_ru[0].value <<< "${RAW_WEATHER}" ) 402 | UPDATE_TIME=$( jq -r .data.current_condition[0].observation_time <<< "${RAW_WEATHER}" |\ 403 | xargs -i date -d 'TZ="UTC" {}' +"%H:%M" 2>/dev/null ) 404 | NOW_EMOJI=$( jq -r .data.current_condition[0].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 405 | 406 | MORNING_C=$( jq -r .data.weather[0].hourly[2].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 407 | NOON_C=$( jq -r .data.weather[0].hourly[5].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 408 | EVENING_C=$( jq -r .data.weather[0].hourly[8].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 409 | MORNING_CMT=$( jq -r .data.weather[0].hourly[2].lang_ru[0].value <<< "${RAW_WEATHER}" ) 410 | NOON_CMT=$( jq -r .data.weather[0].hourly[5].lang_ru[0].value <<< "${RAW_WEATHER}" ) 411 | EVENING_CMT=$( jq -r .data.weather[0].hourly[8].lang_ru[0].value <<< "${RAW_WEATHER}" ) 412 | MORNING_EMOJI=$( jq -r .data.weather[0].hourly[2].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 413 | NOON_EMOJI=$( jq -r .data.weather[0].hourly[5].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 414 | EVENING_EMOJI=$( jq -r .data.weather[0].hourly[8].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 415 | 416 | T_MORNING_C=$( jq -r .data.weather[1].hourly[2].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 417 | T_NOON_C=$( jq -r .data.weather[1].hourly[5].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 418 | T_EVENING_C=$( jq -r .data.weather[1].hourly[8].tempC <<< "${RAW_WEATHER}" | sed '/^[0-9]/s/^/+/' ) 419 | T_MORNING_CMT=$( jq -r .data.weather[1].hourly[2].lang_ru[0].value <<< "${RAW_WEATHER}" ) 420 | T_NOON_CMT=$( jq -r .data.weather[1].hourly[5].lang_ru[0].value <<< "${RAW_WEATHER}" ) 421 | T_EVENING_CMT=$( jq -r .data.weather[1].hourly[8].lang_ru[0].value <<< "${RAW_WEATHER}" ) 422 | T_MORNING_EMOJI=$( jq -r .data.weather[1].hourly[2].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 423 | T_NOON_EMOJI=$( jq -r .data.weather[1].hourly[5].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 424 | T_EVENING_EMOJI=$( jq -r .data.weather[1].hourly[8].weatherCode <<< "${RAW_WEATHER}" | _get_emoji ) 425 | 426 | 427 | 428 | if [[ "${CITY}" == "null" ]] || [[ "${CITY}" == "" ]] 429 | then 430 | PARSE_MODE="none" 431 | if [[ ! -z ${ARGS} ]]; then 432 | TEXT="Wrong location!" 433 | elif [[ "${ADMINS[*]}" =~ ${SENDER} ]]; then 434 | TEXT="You didn't set the default city 435 | You can add default city by this command: 436 | /manage insert into locations(username,city) values(\"default_city\",\"YOUR CITY HERE\")" 437 | 438 | else 439 | TEXT="Administrator didn't set the default city 440 | Try /w City" 441 | fi 442 | else 443 | TEXT=$( echo -e \ 444 | "*Now:* 445 | *[${UPDATE_TIME}]:* ${TEMP} ${NOW_EMOJI} ${COMMENT} 446 | ${CITY} 447 | 448 | *Today:* 449 | *Morning:* ${MORNING_C} ${MORNING_EMOJI} ${MORNING_CMT} 450 | *Noon:* ${NOON_C} ${NOON_EMOJI} ${NOON_CMT} 451 | *Evening:* ${EVENING_C} ${EVENING_EMOJI} ${EVENING_CMT} 452 | 453 | *Tommorow:* 454 | *Morning:* ${T_MORNING_C} ${T_MORNING_EMOJI} ${T_MORNING_CMT} 455 | *Noon:* ${T_NOON_C} ${T_NOON_EMOJI} ${T_NOON_CMT} 456 | *Evening:* ${T_EVENING_C} ${T_EVENING_EMOJI} ${T_EVENING_CMT} 457 | ") 458 | fi 459 | 460 | _send_text "${TEXT}" 461 | _log_echo "Weather \"${CITY}\" to @${SENDER}" 462 | ;; 463 | 464 | #=== COMMAND ================================================================= 465 | # NAME: /info 466 | # DESCRIPTION: Information about this bot 467 | # PARAMETERS: 468 | #=============================================================================== 469 | /info) 470 | 471 | TEXT=$( sed 's/^\s\+//' <<<\ 472 | "This is my first bot on Bash. 473 | You can see the code here https://github.com/Cuttlerat/bashbot 474 | by @Cuttlerat" ) 475 | _send_text "${TEXT}" 476 | ;; 477 | 478 | #=== COMMAND ================================================================= 479 | # NAME: /start 480 | # DESCRIPTION: List of commands 481 | # PARAMETERS: 482 | #=============================================================================== 483 | /start) 484 | 485 | TEXT=$( sed 's/^ \+//' <<<" 486 | Commands: 487 | /weather - Weather in a city for now, today and tommorow 488 | /w - Short form of /w 489 | /info - Information about this bot 490 | /wset - Set default city for /weather 491 | /ibash - Random quote from ibash.org.ru 492 | /loglist - Random quote from loglist.net 493 | /roll - Rolling a random number from 0 to 99 494 | /google - Send a link to google.com/String 495 | /md5 - Creating a md5 sum of String 496 | /manage - Manage your database (Only for admins) 497 | " ) 498 | 499 | _send_text "${TEXT}" 500 | ;; 501 | 502 | #=== COMMAND ================================================================= 503 | # NAME: /ibash 504 | # DESCRIPTION: Random quote from ibash.org.ru 505 | # PARAMETERS: Number of quotes 506 | #=============================================================================== 507 | /ibash) 508 | 509 | _qcycle "ibash" 510 | ;; 511 | 512 | #=== COMMAND ================================================================= 513 | # NAME: /loglist 514 | # DESCRIPTION: Random quote from loglist.net 515 | # PARAMETERS: Number of quotes 516 | #=============================================================================== 517 | /loglist) 518 | 519 | _qcycle "loglist" 520 | ;; 521 | 522 | #=== COMMAND ================================================================= 523 | # NAME: /wset 524 | # DESCRIPTION: Configure a default city for /weather command 525 | # for user who call it 526 | # PARAMETERS: City or "Delete" 527 | #=============================================================================== 528 | /wset) 529 | 530 | if [[ $(sqlite3 "${DATABASE}" \ 531 | "SELECT EXISTS( 532 | SELECT 1 FROM locations 533 | WHERE \"${SENDER}\" 534 | LIKE locations.username 535 | ) LIMIT 1;" \ 536 | ) -eq 1 ]]; then 537 | if [[ -z ${ARGS} ]] || [[ ${ARGS} =~ [Dd]elete ]]; then 538 | sqlite3 "${DATABASE}" \ 539 | "DELETE FROM locations 540 | WHERE \"${SENDER}\" 541 | LIKE locations.username" 542 | TEXT="Deleted information about @${SENDER}" 543 | else 544 | sqlite3 "${DATABASE}" \ 545 | "UPDATE locations 546 | SET city=\"${ARGS}\" 547 | WHERE username=\"${SENDER}\"" 548 | TEXT="New city for @${SENDER}: ${ARGS}" 549 | fi 550 | else 551 | if [[ -z ${ARGS} ]] || [[ ${ARGS} =~ [Dd]elete ]]; then 552 | TEXT="No information about @${SENDER}" 553 | else 554 | sqlite3 "${DATABASE}" \ 555 | "INSERT INTO locations(username, city) 556 | VALUES(\"${SENDER}\",\"${ARGS}\")" 557 | TEXT="Added @${SENDER}: ${ARGS}" 558 | fi 559 | fi 560 | _send_text "${TEXT}" 561 | _log_echo "$TEXT" 562 | ;; 563 | 564 | #=== COMMAND ================================================================= 565 | # NAME: /md5 566 | # DESCRIPTION: Encode a message into md5 567 | # PARAMETERS: String 568 | #=============================================================================== 569 | /md5) 570 | 571 | TEXT=$( md5sum <<< "${ARGS}" | awk '{print $1}' ) 572 | _send_text "${TEXT}" 573 | _log_echo "MD5 to @${SENDER}" 574 | ;; 575 | 576 | #=== COMMAND ================================================================= 577 | # NAME: /google 578 | # DESCRIPTION: Sending a link into google on a entered string 579 | # PARAMETERS: String 580 | #=============================================================================== 581 | /google) 582 | 583 | ARGS=${ARGS//\?/} 584 | PREVIEW=0 585 | TEXT="https://www.google.ru/search?q=${ARGS// /+}" 586 | _send_text "${TEXT}" 587 | _log_echo "Google: \"${ARGS}\" by @${SENDER}" 588 | PREVIEW=1 589 | ;; 590 | 591 | #=== COMMAND ================================================================= 592 | # NAME: /show 593 | # DESCRIPTION: Sending a Rick and Morty yotube video 594 | # PARAMETERS: 595 | #=============================================================================== 596 | /show) 597 | 598 | PREVIEW=0 599 | TEXT="https://www.youtube.com/watch?v=I1188GO4p1E" 600 | _send_text "${TEXT}" 601 | _log_echo "Shchwifty by @${SENDER}" 602 | PREVIEW=1 603 | ;; 604 | 605 | #=== COMMAND ================================================================= 606 | # NAME: /roll 607 | # DESCRIPTION: Rolling a random number from 0 to 99 608 | # PARAMETERS: 609 | #=============================================================================== 610 | /roll) 611 | 612 | ARGS=$( sed 's/.*[^0-9].*/100/' <<< "${ARGS:-100}" ) 613 | TEXT=$(( RANDOM % ARGS )) 614 | _send_text "${TEXT}" 615 | _log_echo "Rolled \"${TEXT}\" by @${SENDER}" 616 | ;; 617 | 618 | #=== COMMAND ================================================================= 619 | # NAME: /ping 620 | # DESCRIPTION: This is not for direct call. This command uses 621 | # to call a user by a regular expression stroed into 622 | # ${DATABASE} 623 | # PARAMETERS: String 624 | #=============================================================================== 625 | /ping) 626 | 627 | 628 | TEXT=$( sqlite3 "${DATABASE}" \ 629 | "SELECT DISTINCT username 630 | FROM pingers 631 | WHERE \"${ARGS,,}\" 632 | LIKE '%'||pingers.match||'%';" ) 633 | if [[ "${TEXT}" =~ EVERYONE\ GET\ IN\ HERE ]]; then 634 | if [[ $( sqlite3 "${DATABASE}" \ 635 | "SELECT EXISTS( 636 | SELECT 1 FROM ping_exclude 637 | WHERE \"${ARGS,,}\" 638 | LIKE '%'||ping_exclude.match||'%' 639 | ) LIMIT 1;" )\ 640 | -eq 1 ]]; then 641 | TEXT=$( sqlite3 "${DATABASE}" \ 642 | "SELECT DISTINCT username 643 | FROM pingers 644 | WHERE \"${TEXT} ${SENDER}\" 645 | NOT LIKE '%'||username||'%'" ) 646 | else 647 | TEXT=$( sqlite3 "${DATABASE}" \ 648 | "SELECT DISTINCT username 649 | FROM pingers 650 | WHERE username 651 | NOT LIKE \"EVERYONE GET IN HERE\" 652 | AND username 653 | NOT LIKE \"${SENDER}\"" ) 654 | fi 655 | fi 656 | if [[ ! -z "${TEXT}" ]]; then 657 | TEXT=$( sed -r 's/\b(.+)\b/@\1/g' <<< "${TEXT}" | xargs ) 658 | _send_text "${TEXT}" 659 | _log_echo "Ping to \"${TEXT}\" by @${SENDER}" 660 | fi 661 | ;; 662 | 663 | #=== COMMAND ================================================================= 664 | # NAME: /answer 665 | # DESCRIPTION: Auto answer on a match in message 666 | # PARAMETERS: String 667 | #=============================================================================== 668 | /answer) 669 | 670 | 671 | while read -r TEXT; do 672 | if [[ ! -z "${TEXT}" ]]; then 673 | _send_text "${TEXT}" 674 | _log_echo "Answer to \"${SENDER}\"" 675 | fi 676 | done < <( sqlite3 "${DATABASE}" \ 677 | "SELECT string FROM answers 678 | WHERE \"${ARGS,,}\" 679 | LIKE '%'||answers.match||'%';" ) 680 | ;; 681 | 682 | #=== COMMAND ================================================================= 683 | # NAME: /manage 684 | # DESCRIPTION: Run a sqlite request 685 | # PARAMETERS: String 686 | #=============================================================================== 687 | /manage) 688 | 689 | if [[ "${ADMINS[*]}" =~ ${SENDER} ]]; then 690 | if [[ "${MESSAGE}" =~ \/manage\ ?$ ]]; then 691 | TEXT="Usage \"/manage COMMAND\"" 692 | else 693 | MESSAGE="$( cut -d' ' -f2- <<< "${MESSAGE}" )" 694 | TEXT=$( sqlite3 "${DATABASE}" "${MESSAGE}" 2>&1 ) 695 | _log_echo "Manage \"${MESSAGE}\" by \"${SENDER}\"" 696 | fi 697 | else 698 | TEXT="You are not an administrator. The incident will be reported" 699 | _log_echo "Trying to manage by \"${SENDER}\"" 700 | fi 701 | _send_text "${TEXT}" 702 | ;; 703 | 704 | #=== COMMAND ================================================================= 705 | # NAME: 706 | # DESCRIPTION: Default message when user calling incorrect command 707 | # PARAMETERS: String 708 | #=============================================================================== 709 | /*) 710 | 711 | if [[ ${PRIVATE} == "true" ]] || [[ $CMD_CHECK =~ /*${BOTNAME} ]]; then 712 | TEXT="Unknown command" 713 | _send_text "${TEXT}" 714 | _log_echo "Bad command \"${CMD_CHECK}\" by @${SENDER}" 715 | fi 716 | ;; 717 | 718 | esac 719 | _offset 720 | done 721 | done 722 | -------------------------------------------------------------------------------- /builder/builder: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is my CI for https://github.com/Cuttlerat/bashbot 4 | 5 | #=== FUNCTION ================================================================ 6 | # NAME: _log_echo 7 | # DESCRIPTION: Echo into log with timestamp 8 | #=============================================================================== 9 | function _log_echo { 10 | 11 | local message="$@" 12 | date +"[%d/%b/%Y:%H:%M:%S %z]: ${message[@]}" 1>&2 13 | 14 | } # ---------- end of function _log_echo ---------- 15 | 16 | cd /bashbot 17 | 18 | while _log_echo "Waiting for updates" &&\ 19 | inotifywait -e modify ./bashbot &>/dev/null; do 20 | 21 | _log_echo "Building new bashbot" 22 | docker-compose build bashbot &>/dev/null \ 23 | || _log_echo "Error while building" 24 | 25 | _log_echo "Creating new container for bashbot" 26 | docker-compose create bashbot &>/dev/null \ 27 | || _log_echo "Error while creating" 28 | 29 | _log_echo "Restarting bashbot" 30 | docker-compose restart bashbot &>/dev/null \ 31 | || _log_echo "Error while restarting" 32 | 33 | done 34 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | 4 | bashbot: 5 | build: 6 | context: . 7 | dockerfile: dockerfiles/bashbot.Dockerfile 8 | volumes: 9 | - ./database:/database 10 | env_file: ./tokens 11 | restart: unless-stopped 12 | 13 | builder: 14 | build: 15 | context: . 16 | dockerfile: dockerfiles/builder.Dockerfile 17 | volumes: 18 | - .:/bashbot 19 | - /var/run/docker.sock:/var/run/docker.sock 20 | restart: unless-stopped 21 | -------------------------------------------------------------------------------- /dockerfiles/bashbot.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.6 2 | MAINTAINER Aleksei Kioller 3 | RUN apk add --update --no-cache \ 4 | jq curl bash sed coreutils \ 5 | tzdata w3m sqlite 6 | RUN cp /usr/share/zoneinfo/Europe/Moscow \ 7 | /etc/localtime 8 | COPY bashbot / 9 | ENTRYPOINT ["/bashbot"] 10 | -------------------------------------------------------------------------------- /dockerfiles/builder.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.6 2 | MAINTAINER Aleksei Kioller 3 | RUN apk add --update --no-cache \ 4 | python3 docker inotify-tools \ 5 | bash py-pip tzdata 6 | RUN pip install 'docker-compose==1.14.0' 7 | RUN cp /usr/share/zoneinfo/Europe/Moscow \ 8 | /etc/localtime 9 | ENTRYPOINT ["/bashbot/builder/builder"] 10 | -------------------------------------------------------------------------------- /first_start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir database 3 | touch database/database.db 4 | chmod 600 database/database.db 5 | if [[ ! -f tokens ]]; then 6 | touch tokens 7 | echo "BOT_TOKEN=YOUR_TELEGRAM_TOKEN_HERE" > tokens 8 | echo "WEATHER_TOKEN=YOUR_WEATHER_KEY_HERE" >> tokens 9 | echo "ADMINS=( \"YOUR TELEGRAM USERNAME WITHOUT @\" )" >> tokens 10 | fi 11 | chmod 600 tokens 12 | rm $0 13 | --------------------------------------------------------------------------------