From 446d9cbec09795ff7c66bedd7d742c7c6cfda42a Mon Sep 17 00:00:00 2001 From: celso Date: Tue, 28 Mar 2023 14:29:56 -0300 Subject: [PATCH] updated comments and added README --- README.md | 160 ++++++++++++++++++++++++++++++++++++++++++++ bashbot-lib.sh | 6 +- images/example1.png | Bin 0 -> 9825 bytes 3 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 README.md create mode 100644 images/example1.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f1498d --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# bashbot-lib +bashbot-lib provides generic functionality for telegram bots made in bash
+with this library you will be able to run a telegram bot with few dependencies
+**IMPORTANT**: bashbot-lib uses the getUpdates method with tcp_keepalive, not webhooks
+**IMPORTANT**: bashbot-lib is not made for asynchronous use, only one message is received at a time and is done so in order
+ +## license +this program is licensed under the **Affero GNU Public License v3**, you can read the copy that comes along with this program or read it at [gnu.org's website](http://www.gnu.org/licenses/agpl-3.0.html) + +## dependencies +the following is all that's needed +* bash 4+ +* GNU coreutils (sed, grep, tail, head, etc.) +* curl +* a telegram API token + +Bash 4+ is obligatory for this library to work as features introduced at this point are heavily used. + +## installation +drop it wherever you like, although I recommend either of these options +* copying the files to /usr/lib/bashbot-lib/ +* add it as a submodule in your project (git submodules are very annoying to deal with) +* simply keep it in a subfolder in your project + +## usage +source the library, and optionally the viewer, in your bash shellscript +``` +. /usr/lib/bashbot-lib/bashbot-lib.sh +. /usr/lib/bashbot-lib/viewer.sh +``` +you will also need the following +* a variable named api_url with the following content (mind the trailing forward slash): `api_url="https:/api.telegram.org/bot${YOUR_TOKEN_HERE}/"` +* the following files to read and write from: `updates.txt` `sentmsgs.txt` `delmsgs.txt` + * these files must be placed in the same directory you run your bot from or in the directory defined in the following bullet poiint +* (optional) a variable named bot_tmpdir with the following content (mind the trailing forward slash): `bot_tmpdir="/some/directory/"` + * if you define this directory, it will hold the files from the previous bullet point +* (optional) a bash associative array for use with the getmsg_content function: `declare -A curmsg` +* (optional) I recommend adding the following at the beggining of your implementation to centralize error logs: `exec 2>"${bot_tmpdir}error.log"` + +once you've got that set up, all that's left is using the functions provided to make your bot + +### getting updates +use the `getupd` function to request an update (singular) from telegram's servers, the output will be printed to `updates.txt` + +if there is no updates for you, the connection will stall for 60 seconds waiting for one and return empty if there was none, this is done to minimize unnecesary network traffic; the code actually requests that the connection stay open for 300 seconds but telegram only does so for 60 anyways + +with every update, getupd calculates the new offset required to request new updates after the last one so no repeated responses clutter `updates.txt` + +`getupd` requires no additional arguments passed to it + +### reading message contents +use the `getmsg_content` function to parse the contents of a message and save them to a bash associative array + +the `getmsg_content` receives two arguments +* the **name** (not a reference to) of a bash associative array +* the filename of a file with telegram JSON responses formatted by this library's functions + +**IMPORANT**: you must pass only the name of the associative array and not a reference to it +``` +# wrong way +getmsg_content ${curmsg} updates.txt +# right way +getmsg_content curmsg updates.txt +``` +this is so because bash associative arrays can only be passed to functions as named references + +the message is read from the last line of the file passed as second argument + +the file will be either updates.txt or sentmsgs.txt most probably, although it will work with any file where the contents are telegram JSON responses formatted with this library's functions, so you can use it whoever you like in your implementation + +the bash associative array will hold the following contents + +| key | value | +| :-------------: | :------------------------------------: | +| chat_id | numbers, begins with - if it's a group | +| chat_title | same as user_name if private message | +| user_id | numbers, begins with - if it's a group | +| user_name | may contain special symbols | +| username | the user's @, may be empty | +| msg_id | a number to reference this message | +| text | may contain special symbols | +| callback | callback_data, empty if there is none | + +### sending messages +use the `sendmsg` function to send messages and print the result to `sentmsgs.txt` + +the `sendmsg` function takes up to three arguments +* the chat_id of the chat to send the message to +* the contents of the message (text only for this version) +* (optional) an inline keyboard made with `mkinline_kbd` + +**IMPORTANT**: you must [URL % encode](https://www.w3schools.com/tags/ref_urlencode.ASP) some special characters in the contents of the message or telegram won't accept them +``` +# wrong way +sendmsg "${curmsg[chat_id]}" "message %% with ۖ special +characters" +# right way +sendmsg "${curmsg[chat_id]}" "message+%25%25+with+%E2%82%AC%C3%B1+special%0Acharacters" +``` +### replying to messages +use the `replymsg` function to reply to messages and print the result to `sentmsgs.txt` + +the `replymsg` function takes up to four arguments: +* the `chat_id` of the chat to send the message to +* the `msg_id` of the message to reply to +* the contents of the message (text only for this version, support for images and media will come in future versions) +* (optional) an inline keyboard made with `mkinline_kbd` + +the same warning about special characters done in the sending messages section applies + +### editing messages +use the `replymsg` function to edit messages and print the result to `sentmsgs.txt` + +the `editmsg` function takes up to four arguments: +* the `chat_id` of the chat to edit the message in +* the `msg_id` of the message to edit +* the new contents of the message (text only for this version, support for images and media will come in future versions) +* (optional) an inline keyboard made with `mkinline_kbd` + +note: the message content must differ from the current one + +the same warning about special characters done in the sending messages section applies + +### deleting mesages +use the `delmsg` function to delete messages and print the result to `delmsgs.txt` + +the `delmsg` function takes two arguments: +* the `chat_id` of the chat where the message is +* the `msg_id` of the message to delete + +### inline keyboards +use the `mkinline_kbd` function to make inline keyboards you can pass to the `sendmsg`, `replymsg` and `editmsg` functions + +the `mkinline_kbd` function takes three arguments: +* the name of a bash array with the ammount of columns in each row +* the name of a bash array with the text to display in each button label +* the name of a bash array with the callback_data each button label sends when pressed + +the keyboard will have as many rows as there are members in the array of the first argument, and as many columns in each of them as the member says: +* `declare -a kbd_rows=( 1 )` will have 1 row with 1 column +* `declare -a kbd_rows=( 3 1 )` will have 2 rows, the first one with 3 columns and the second one with 1 + +the text and data are added in order left to right, up to down, on each column of the keyboard + +this function may require an example to be better understood, so please take a look at the following + +``` +declare -a kbd_rows=( 1 3 3) +declare -a kbd_text=( "apple" "orange" "banana" "peach" "cherry" "pear" "lemon" ) +declare -a kbd_data=( "red" "orange" "yellow" "orange" "red" "green" "yellow" ) + +mkinline_kbd kbd_rows kbd_text kbd_data +``` +will result in +``` +{"inline_keyboard":[[{"text":"apple","callback_data":"red"}],[{"text":"orange","callback_data":"orange"},{"text":"banana","callback_data":"yellow"},{"text":"peach","callback_data":"orange"}],[{"text":"cherry","callback_data":"red"},{"text":"pear","callback_data":"green"},{"text":"lemon","callback_data":"yellow"}]]} +``` +if you use this with sendmsg, the keyboard will look like this + +![why are you blind](./images/example1.png "example 1 image") diff --git a/bashbot-lib.sh b/bashbot-lib.sh index bd01fe3..ae0fc5a 100755 --- a/bashbot-lib.sh +++ b/bashbot-lib.sh @@ -5,13 +5,11 @@ # 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 bot id as -# bot_id="[insert your bot_id]" -# 3. the following files to read and write from +# 2. the following files to read and write from # updates.txt # sentmsgs.txt # delmsgs.txt -# 4. (optional) a directory for the bot's files as +# 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 diff --git a/images/example1.png b/images/example1.png new file mode 100644 index 0000000000000000000000000000000000000000..31739ff3ffa0cd381272ecc4c394470302820fd2 GIT binary patch literal 9825 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 literal 0 HcmV?d00001