2019-05-16 14:43:44 +00:00
#### [Home](../README.md)
## Bashbot function reference
### Send, forward, delete messages
2020-12-25 16:55:46 +00:00
To insert line brakes in a message or caption you can place `\n` in the text.
2019-05-16 14:43:44 +00:00
##### send_action
2020-12-29 09:18:41 +00:00
`send_action` shows users what your bot is currently doing.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* send_action "CHAT[ID]" "action"
2019-05-16 14:43:44 +00:00
2020-12-29 09:18:41 +00:00
*"action":* `typing` , `upload_photo` , `record_video` , `upload_video` , `record_audio` , `upload_audio` , `upload_document` , `find_location` .
2019-05-16 14:43:44 +00:00
*alias:* _action "action"
*example:*
```bash
send_action "${CHAT[ID]}" "typing"
send_action "${CHAT[ID]}" "record_audio"
```
##### send_normal_message
2020-12-29 09:18:41 +00:00
`send_normal_message` sends text only messages to the given chat.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* send_normal_message "CHAT[ID]" "message"
2019-05-16 14:43:44 +00:00
*alias:* _normal_message "message"
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a text message"
```
2020-12-13 18:10:12 +00:00
##### send_markdownv2_message
2020-12-29 09:18:41 +00:00
`send_markdownv2_message` sends markdown v2 style messages to the given chat.
2020-12-13 18:10:12 +00:00
Telegram supports a new [Markdown V2 Style ](https://core.telegram.org/bots/api#markdownv2-style ) which
has more formatting codes and is more robust, but incompatible with old telegram markdown style.
2021-01-02 12:13:01 +00:00
To send characters reserved for markdown v2 formatting, you must prefix them with `\` ( e.g. `\| \= \_ \*` ).\
2021-01-02 12:08:02 +00:00
*Hint*: If a message is not sent, have a look in `logs/ERROR.log`
2021-01-25 19:34:50 +00:00
*usage:* send_markdownv2_message "CHAT[ID]" "markdown message"
2020-12-13 18:10:12 +00:00
*example:*
```bash
send_markdownv2_message "${CHAT[ID]}" "this is a markdown message, next word is *bold* "
send_markdownv2_message "${CHAT[ID]}" "*bold* __underlined__ [text ](link )"
```
2019-05-16 14:43:44 +00:00
##### send_markdown_message
2020-12-29 09:18:41 +00:00
`send_markdown_message` sends markdown style messages to the given chat.
2020-12-13 19:36:16 +00:00
This is the old, legacy Telegram markdown style, retained for backward compatibility.
2020-12-13 18:10:12 +00:00
It supports a [reduced set of Markdown ](https://core.telegram.org/bots/api#markdown-style ) only
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* send_markdown_message "CHAT[ID]" "markdown message"
2019-05-16 14:43:44 +00:00
*alias:* _markdown "message"
*example:*
```bash
send_markdown_message "${CHAT[ID]}" "this is a markdown message, next word is *bold* "
send_markdown_message "${CHAT[ID]}" "*bold* _italic_ [text ](link )"
```
2020-12-13 18:10:12 +00:00
2019-05-16 14:43:44 +00:00
##### send_html_message
2020-12-29 09:18:41 +00:00
`send_html_message` sends HTML style messages to the given chat.
2019-05-16 14:43:44 +00:00
Telegram supports a [reduced set of HTML ](https://core.telegram.org/bots/api#html-style ) only
2021-01-25 19:34:50 +00:00
*usage:* send_html_message "CHAT[ID]" "html message"
2019-05-16 14:43:44 +00:00
*alias:* _html_message "message"
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a markdown message, next word is < b > bold< / b > "
send_normal_message "${CHAT[ID]}" "< b > bold< / b > < i > italic>< i > < em > italic>/em> < a href = "link" > Text< / a > "
```
##### forward_message
2020-12-29 09:18:41 +00:00
`forward_mesage` forwards a message to the given chat.
2019-05-16 14:43:44 +00:00
*usage:* forward_message "chat_to" "chat_from" "${MESSAGE[ID]}"
*old call:* forward "${CHAT[ID]}" "$FROMCHAT" "${MESSAGE[ID]}"
*alias:* _forward "$FROMCHAT" "${MESSAGE[ID]}"
2020-06-23 14:35:50 +00:00
See also [Text formatting options ](https://core.telegram.org/bots/api#formatting-options )
2019-05-16 14:43:44 +00:00
----
##### delete_message
2019-06-13 15:02:40 +00:00
A bot can only delete messages if he is admin of a Chat, if not he can delete his own messages only.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* delete_message "CHAT[ID]" "${MESSAGE[ID]}"
2019-05-16 14:43:44 +00:00
See also [deleteMessage limitations ](https://core.telegram.org/bots/api#deletemessage )
----
2019-05-20 08:50:51 +00:00
##### send_message
2020-12-29 09:18:41 +00:00
`send_message` sends any type of message to the given chat. Type of output is steered by keywords within the message.
2019-05-20 08:50:51 +00:00
The main use case for send_message is to process the output of interactive chats and background jobs. **For regular Bot commands I recommend using of the dedicated send_xxx_message() functions from above.**
2021-01-25 19:34:50 +00:00
*usage:* send_message "CHAT[ID]" "message"
2019-05-20 08:50:51 +00:00
*example:* - see [Usage ](2_usage.md#send_message ) and [Advanced Usage ](3_advanced.md#Interactive-Chats )
----
2020-06-29 12:55:08 +00:00
### File, Album, Location, Venue, Keyboard
2019-05-16 14:43:44 +00:00
##### send_file
2021-01-06 16:22:25 +00:00
send_file can send local files, URL's or file_id's as different filex types (_e.g. photo video sticker_)
2020-12-27 09:49:51 +00:00
2021-01-25 19:34:50 +00:00
*usage:* send_file "CHAT[ID]" "file/URL/file_id" "caption" ["type"]
2019-05-16 14:43:44 +00:00
2021-01-06 17:10:58 +00:00
URL's must start with `http://` or `https://` and remote server must send an appropriate media type.
A file_id must start with `file_id://` , all other file names are threated as local files.
If Telegram accepts the file `BOTSENT[FILE_ID]` and `BOTSENT[FILE_TYPE]` are set.
2021-01-05 21:35:34 +00:00
Argument "type" is optional, if not given `send_file` detects file type by the file extension.
2021-01-06 16:22:25 +00:00
if file/URL has no extension `photo` is assumed. Unknown types and extensions are send as type `document`
2021-01-05 21:35:34 +00:00
Supported file types are: photo (_png jpg jpeg gif pic_) audio (_mp3 flac_) sticker (_webp_) video (_mp4_) voice (_ogg_) or document.
It's recommended to use __absolute path names__ for local files (_starting with `/` _), as relative path names are threated as __relative to UPLOADDIR__ `data-bot-bash/upload` !
For security reasons the following restrictions apply to local files:
2020-12-27 09:38:49 +00:00
2021-01-06 16:22:25 +00:00
- absolute path name must match the __shell regex__ `FILE_REGEX`
- relative path name is threated as relative to `UPLOADDIR` (_default: data-bot-bash/upload_)
2020-12-27 09:49:51 +00:00
- path must not start with `./` and not contain `../`
2019-05-22 21:22:11 +00:00
2019-05-16 14:43:44 +00:00
2020-06-29 12:55:08 +00:00
*example:*
2019-05-16 14:43:44 +00:00
```bash
2021-01-05 21:35:34 +00:00
# send picture from web
2021-01-06 16:22:25 +00:00
send_file "${CHAT[ID]}" "https://dealz.rrr.de/assets/images/rbofd-1.gif" "My Bot" "photo"
send_file "${CHAT[ID]}" "https://images-na.ssl-images-amazon.com/images/I/81DQ0FpoSNL._AC_SL1500_.jpg"
2021-01-05 21:35:34 +00:00
# local file recommended: absolute path
2020-12-27 09:38:49 +00:00
send_file "${CHAT[ID]}" "/home/user/dog.jpg" "My Dog"
2020-12-27 09:49:51 +00:00
# relative to UPLOADDIR: data-bot-bash/upload/dog.jpg
2020-12-27 09:38:49 +00:00
send_file "${CHAT[ID]}" "dog.jpg" "My Dog"
2020-12-27 09:49:51 +00:00
# change to personal upload dir
2020-12-27 09:38:49 +00:00
UPLOADDIR="/home/user/myuploaddir"
2020-12-27 09:49:51 +00:00
# relative to personal upload dir: /home/user/myuploaddir/dog.jpg
2020-12-27 09:38:49 +00:00
send_file "${CHAT[ID]}" "dog.jpg" "My Dog"
2019-05-16 14:43:44 +00:00
```
2020-06-29 12:55:08 +00:00
##### send_album
2021-01-25 19:34:50 +00:00
*usage:* send_album "CHAT[ID]" "URL1" "URL2" ... "URLn"
2020-06-29 12:55:08 +00:00
*example:*
```bash
send_album "$(getConfigKey "botadmin")" "http://www.rrr.de/slider/main-image1.jpg" "http://www.rrr.de/slider/main-image5.jpg"
```
2019-05-16 14:43:44 +00:00
##### send_location
2021-01-25 19:34:50 +00:00
*usage:* send_location "CHAT[ID]" "Latitude" "Longitude"
2019-05-16 14:43:44 +00:00
##### send_venue
2021-01-25 19:34:50 +00:00
*usage:* send_venue "CHAT[ID]" "Latitude" "Longitude" "Title" "Address" "foursquare id (optional)"
2019-05-16 14:43:44 +00:00
2021-01-24 13:39:36 +00:00
##### send_sticker
`send_sticker` sends a sticker using a `file_id` to send a sticker that exists on the Telegram servers.
*usage:* send_sticker "$CHAT[ID]" "file_id"
2019-05-16 14:43:44 +00:00
----
##### send_keyboard
2021-01-24 13:39:36 +00:00
`send_keyboard` sends a custom keyboard, Telegram clients will show it instead of the regular keyboard.
If the user press a button on the custom keyboard, the text shown on the button is send to the chat.
2020-08-08 15:58:18 +00:00
Example Keyboard Array definitions:
2019-05-16 14:43:44 +00:00
- Yes No in one row: '[ "yes" , "no" ]'
- Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]'
2020-06-23 14:11:45 +00:00
- number pad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]'
2019-05-16 14:43:44 +00:00
*usage:* send_keyboard "chat-id" "message" "keyboard"
*alias:* _keyboard "message" "keyboard"
*example:*
```bash
2019-06-13 15:02:40 +00:00
send_keyboard "${CHAT[ID]}" "Say yes or no" '[ "yes" , "no" ]' # in one row
send_keyboard "${CHAT[ID]}" "Say yes or no" '[ "yes" ] , [ "no" ]' # 2 rows
send_keyboard "${CHAT[ID]}" "Enter digit" '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]'
_keyboard_yesno # see aliases
_keyboard_numpad
2019-05-16 14:43:44 +00:00
```
##### remove_keyboard
2021-01-24 13:15:01 +00:00
`remove_keyboard` deletes the last custom keyboard. Depending on used Telegram client this will hide or delete the custom keyboard.
2019-05-16 14:43:44 +00:00
*usage:* remove_keybord "$CHAT[ID]" "message"
*alias:* _del_keyboard "message"
*See also: [Keyboard Markup ](https://core.telegram.org/bots/api/#replykeyboardmarkup )*
2021-01-19 20:55:39 +00:00
2021-01-25 13:52:47 +00:00
----
2019-05-16 14:43:44 +00:00
##### send_button
2021-01-25 13:52:47 +00:00
`send_button` sends a text message with a single button to open an URL attached.
2021-01-20 15:21:46 +00:00
2021-01-14 19:57:23 +00:00
*usage:* send_button "$CHAT[ID]" "message" "text" "URL"
2019-05-16 14:43:44 +00:00
*alias:* _button "text" "URL"
*example:*
```bash
2021-01-20 15:21:46 +00:00
send_button "${CHAT[ID]}" "Awesome Deals!" "Visit my Shop" "https://dealz.rrr.de"
2019-05-16 14:43:44 +00:00
```
2021-01-27 06:36:26 +00:00
### Inline buttons
Functions to send/edit messages with with some buttons attached.
2021-01-26 19:53:08 +00:00
##### send_inline_buttons
2021-01-27 06:36:26 +00:00
`senbd_inline_buttons` sends a message with multiple buttons attached. Buttons can be an URL or a CALLBACK button.
By default all buttons are displayed on one row, an empty string `""` starts a new row.
2021-01-20 15:21:46 +00:00
2021-01-26 19:53:08 +00:00
*usage:* send_inline_buttons "CHAT[ID]" "text|url" "text|url" "" "url" "" "text|url" ...
2021-01-20 15:21:46 +00:00
2021-01-27 06:36:26 +00:00
URL buttons are specified as a `"text|url"` pair separated by `|` , `text` is shown on the button and `url` is opened on button click.
2021-01-20 15:27:59 +00:00
If `"url"` without text is given, `url` is shown on the button and opened on button click.
2021-01-20 15:21:46 +00:00
2021-01-27 06:36:26 +00:00
*Important* An `url` not startung with http(s):// or tg:// will create a
[CALLBACK Button ](https://core.telegram.org/bots/2-0-intro#callback-buttons ).
2021-01-24 12:52:44 +00:00
2021-01-20 15:21:46 +00:00
*example:*
```bash
# one button, same as send_button
2021-01-27 10:54:48 +00:00
send_inline_buttons "${CHAT[ID]}" "Best Dealz!" "Visit my Shop|https://dealz.rrr.de"
2021-01-20 15:21:46 +00:00
2021-01-27 06:54:28 +00:00
# result
Best Dealz!
+----------------------------+
| Visit my Shop |
+----------------------------+
2021-01-20 15:21:46 +00:00
# one button row
2021-01-27 10:54:48 +00:00
send_inline_buttons "${CHAT[ID]}" "message" "Button 1|http://rrr.de" "Button 2|http://rrr.de"
2021-01-27 06:54:28 +00:00
# result
message ...
+----------------------------+
| Button 1 | Button 2 |
+----------------------------+
2019-05-16 14:43:44 +00:00
2021-01-20 15:21:46 +00:00
# multiple button rows
2021-01-27 10:54:48 +00:00
send_inline_buttons "${CHAT[ID]}" "message" "Button 1|http://rrr.de" "Button 2|http://rrr.de" "" "Button on second row|http://rrr.de"
2021-01-27 06:54:28 +00:00
# result
message ...
+----------------------------+
| Button 1 | Button 2 |
|----------------------------|
| Button on second row |
+----------------------------+
2021-01-27 10:54:48 +00:00
2019-05-16 14:43:44 +00:00
```
2021-01-26 19:53:08 +00:00
##### edit_inline_buttons
2021-01-27 06:36:26 +00:00
`edit_inline_buttons` add inline buttons to existing messages, existing inline buttons will be replaced.
Only the attached buttons will be changed, not the message.
2021-01-22 22:44:15 +00:00
2021-01-26 19:53:08 +00:00
*usage:* edit_inline_buttons "CHAT[ID]" "MESSAGE[ID]" "text|url" "text|url" ...
2021-01-22 22:44:15 +00:00
2021-01-26 13:51:50 +00:00
*example:*
2021-01-22 22:44:15 +00:00
```bash
# message without button
send_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message ..."
echo ${BOTSEND[ID]}
567
# add one button row
2021-01-26 19:53:08 +00:00
edit_inline_keyboard "${CHAT[ID]}" "567" "button 1|http://rrr.de" "button 2|http://rrr.de"
2021-01-22 22:44:15 +00:00
# change buttons
2021-01-26 19:53:08 +00:00
edit_inline_keyboard "${CHAT[ID]}" "567" "Success edit_inline_keyboard|http://rrr.de"
2021-01-22 22:44:15 +00:00
# delete button by replace whole message
edit_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message inline *removed* ..."
```
2021-01-26 13:51:50 +00:00
##### answer_callback_query
2021-01-27 06:36:26 +00:00
Each request send from a CALLBACK button must be answered by a call to `answer_callback_query` .
2021-01-26 13:51:50 +00:00
If alert is given an alert will be shown by the Telegram client instead of a notification.
*usage:* answer_callback_query "iBUTTON[ID]" "text notification ..." ["alert"]
*example:*
```bash
answer_callback_query "${iBUTTON[ID]}" "Button data is: ${iBUTTON[DATA]}"
answer_callback_query "${iBUTTON[ID]}" "Alert: Button pressed!" "alert"
```
2021-01-22 22:44:15 +00:00
2021-01-27 10:54:48 +00:00
```bash
# CALLBACK button example
send_inline_buttons "${CHAT[ID]}" "Press Button ..." " Button |RANDOM-BUTTON"
# result
Press Button ...
+----------------------------+
| Button |
+----------------------------+
# react on button press from mycommands
CALLBACK="1" # enable callbacks
...
mycallbacks() {
local answer
#######################
# callbacks from buttons attached to messages will be processed here
if [ "${iBUTTON[DATA]}" = "RANDOM-BUTTON" ]; then
answer="Button pressed"
edit_inline_buttons "${iBUTTON[CHAT_ID]}" "${iBUTTON[MESSAGE_ID]}" " Button ${RANDOM}|RANDOM-BUTTON"
fi
# Telegram needs an ack each callback query, default empty
answer_callback_query "${iBUTTON[ID]}" "${answer}"
;;
}
# result, XXXXX: random number changed on each press
Press Button ...
+----------------------------+
| Button XXXXXX |
+----------------------------+
```
2020-12-13 18:10:12 +00:00
----
2021-01-27 06:36:26 +00:00
#### Inline keyboards
Functions to send/edit more complex button layouts (keyboards), I suggest to start with the simpler inline buttons above.
##### _button_row
`_button_row` is a helper function to specify a keyboard row in the form "text|url" pairs.
Internally used by inline buttons also.
*usage:* _button_row "text|url" "text|url" "url" "text|url" ...
*example:*
```bash
# similar to send_button
send_inline_keyboard "${CHAT[ID]}" "Best Dealz!" "$(_button_row "Visit my Shop|https://dealz.rrr.de")"
# similar to send_inline_button
send_inline_keyboard "${CHAT[ID]}" "message" "$(_button_row "button 1|http://rrr.de" "button 2|http://rrr.de")"
# multiple button rows
send_inline_keyboard "${CHAT[ID]}" "message" "$(_button_row "b1|http://rrr.de" "b2|http://rrr.de" "" "b3|http://rrr.de" "b4|http://rrr.de")"
```
##### send_inline_keyboard
`send_inline_keyboard` sends a message with keyboards attached, keyboards must be specified in JSON format.
*usage:* send_inline_keyboard "CHAT[ID]" "message" "[JSON button array]"
I suggest to use `_button_row` to create the used JSON. For hand crafted JSON the following format must be used,
see [Inline Keyboard Markup ](https://core.telegram.org/bots/api#inlinekeyboardmarkup )
URL `[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ],[...]` \
CALLBACK `[ {"text":"text1", "callback_data":"abc"}, ... {"text":"textN", "callback_data":"defg"} ],[...]` \
An URL Button opens the given URL, a CALLBACK button sends an update the bot must react on.
*example:*
```bash
# send_button
send_inline_keyboard "${CHAT[ID]}" "Best Dealz!" '[{"text":"Visit my Shop", "url":"https://dealz.rrr.de"}]'
# send_inline_button
send_inline_keyboard "${CHAT[ID]}" "message" '[{"text":"button 1", url"":"http://rrr.de"}, {"text":"button 2", "url":"http://rrr.de"} ]'
# multiple button rows
send_inline_keyboard "${CHAT[ID]}" "message" '[{"text":"b1", "url":"http://rrr.de"}, {"text":"b2", "url":"http://rrr.de"}], [{"text":"b3", "url":"http://rrr.de"}, "text":"b4", "url":"http://rrr.de"}]'
# more complex keyboard, note the ,
keyboard_text="Deal-O-Mat public groups ..."
keyboard_json="$(_button_row "🤖 #Home of Deal-O-Mat Bot 🤖|https://dealz.rrr.de/dealzbot.html")
, $(_button_row "Amazon DE|https://t.me/joinchat/IvvRtlxxxxx" "Home & Family|https://t.me/joinchat/VPh_wexxxxx")
, $(_button_row "Amz International |https://t.me/joinchat/IvvRtkxxxxx" "Amazon WHD|https://t.me/joinchat/IvvRxxxxx")
, $(_button_row "Smartphones|https://t.me/joinchat/IvvRthtqxxxxx" "Gaming|https://t.me/joinchat/IvvRthRyrsmxxxxx")
, $(_button_row "Accessoires|https://t.me/joinchat/IvvRthlJxxxxx" "eBay|https://t.me/joinchat/IvvRthxxxxx")
, $(_button_row "!! Offtopic Discussions !!|https://t.me/joinchat/IvvRthRhxxxxx-pZrWw")
, $(_button_row "Deals >100|https://t.me/joinchat/IvvRtxxxxx" "Leasing|https://t.me/joinchat/IvvRthRbxxxxx")
, $(_button_row "Deals >1000|https://t.me/joinchat/IvvRtlxxxxx" "Deals >500|https://t.me/joinchat/IvvRthvbHxxxxx")
send_inline_keyboard "CHAT[ID]" "${keyboard_text}" "${keyboard_json}"
2021-01-27 06:54:28 +00:00
# result
2021-01-27 06:36:26 +00:00
+---------------------------------+
| 🤖 #Home of Deal-O-Mat Bot 🤖 |
|---------------------------------|
| Amazon DE | Home & Family |
|----------------|----------------|
| Amz Internat | Amazon WHD |
|----------------|----------------|
| Smartphones | Gaming |
|----------------|----------------|
| Accessoires | eBay |
|---------------------------------|
| !! Offtopic Discussions !! |
|---------------------------------|
| Deals >100 | Leasing |
|----------------|----------------|
| Deals >1000 | Deals >500 |
+---------------------------------+
```
*See also [Inline keyboard markup ](https://core.telegram.org/bots/api/#inlinekeyboardmarkup )*
##### edit_inline_keyboard
`edit_inline_keyboard` add inline keyboards to existing messages and replace existing inline keyboards.
Only the attached keyboard will be changed, not the message.
*usage:* edit_inline_keyboard "CHAT[ID]" "MESSAGE[ID]" "[JSON button array]"
To create a JSON button array I suggest to use `_button_row` .
*example:*
```bash
# message without button
send_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message ..."
echo ${BOTSEND[ID]}
567
# add one button row with help of _button_row
edit_inline_keyboard "${CHAT[ID]}" "567" "$(_button_row "button 1|http://rrr.de" "button 2|http://rrr.de")"
# change buttons with help of _button_row
edit_inline_keyboard "${CHAT[ID]}" "567" "$(_button_row "Success edit_inline_keyboard|http://rrr.de")"
# delete button by replace whole message
edit_markdownv2_message "${CHAT[ID]}" "*HI* this is a _markdown_ message inline *removed* ..."
```
----
2021-01-26 19:53:08 +00:00
2020-12-13 18:10:12 +00:00
### Edit / Replace Messages
2020-12-14 13:00:23 +00:00
Edit a message means replace the content of the message in place. The message stay on the same position in the chat and keep the same
2020-12-13 18:10:12 +00:00
message id.
2021-01-10 19:46:56 +00:00
If new message is the same than current message Telegram return error 400 with description "Bad Request: chat message is not modified"
2020-12-13 18:10:12 +00:00
2020-12-13 19:36:16 +00:00
There is no need to use the same format when replace a message, e.g. a message sent with `send_normal_message` can be replaced with
2020-12-13 18:10:12 +00:00
`edit_markdown_message` or `edit_html_message` and vice versa.
2020-12-13 19:36:16 +00:00
To replace a message you must know the message id of the the original message. The best way to get the message id is to save the value of
`BOTSENT[ID]` after sending the original message.
2020-12-13 18:10:12 +00:00
##### edit_normal_message
2020-12-29 09:18:41 +00:00
`edit_normal_message` replace a message with a text message in the given chat.
2020-12-13 18:10:12 +00:00
2021-01-25 19:34:50 +00:00
*usage:* edit_normal_message "CHAT[ID]" "MESSAGE-ID" "message"
2020-12-13 18:10:12 +00:00
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a text message"
saved-id="${BOTSENT[ID]}"
edit_normal_message "${CHAT[ID]}" "${saved-id}" "this is another text"
```
2020-12-13 19:36:16 +00:00
##### edit_markdownv2_message
2020-12-29 09:18:41 +00:00
`edit_markdownv2_message` replace a message with a markdown v2 message in the given chat.
2020-12-13 18:10:12 +00:00
2021-01-25 19:34:50 +00:00
*usage:* edit_markdownv2_message "CHAT[ID]" "MESSAGE-ID" "message"
2020-12-13 18:10:12 +00:00
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a text message"
saved-id="${BOTSENT[ID]}"
edit_markdownv2_message "${CHAT[ID]}" "${saved-id}" "this is __markdown__ *V2* text"
```
2020-12-13 19:36:16 +00:00
##### edit_markdown_message
2020-12-29 09:18:41 +00:00
`edit_markdown_message` replace a message with a markdown message in the given chat.
2020-12-13 18:10:12 +00:00
2021-01-25 19:34:50 +00:00
*usage:* edit_markdown_message "CHAT[ID]" "MESSAGE-ID" "message"
2020-12-13 18:10:12 +00:00
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a text message"
saved-id="${BOTSENT[ID]}"
edit_markdown_message "${CHAT[ID]}" "${saved-id}" "this is *markdown* text"
```
##### edit_html_message
2020-12-29 09:18:41 +00:00
`edit_html_message` replace a message with a html message in the given chat.
2020-12-13 18:10:12 +00:00
2021-01-25 19:34:50 +00:00
*usage:* edit_html_message "CHAT[ID]" "MESSAGE-ID" "message"
2020-12-13 18:10:12 +00:00
*example:*
```bash
send_normal_message "${CHAT[ID]}" "this is a text message"
saved-id="${BOTSENT[ID]}"
edit_html_message "${CHAT[ID]}" "${saved-id}" "this is < b > html< / b > text"
```
2021-01-14 19:57:23 +00:00
##### edit_message_caption
`edit_message_caption` changes the caption of a message (photo, audio, video, document) in the given chat.
2021-01-25 19:34:50 +00:00
*usage:* edit_message_caption "CHAT[ID]" "MESSAGE-ID" "caption"
2021-01-14 19:57:23 +00:00
2020-12-13 18:10:12 +00:00
2021-02-06 16:19:07 +00:00
----
### Get files from Telegram
##### download_file
2021-02-06 16:38:37 +00:00
`download_file` download a file to `DATADIR` and returns the local `path` to the file on disc, main use is to download files send to chats.
2021-02-06 16:19:07 +00:00
I tried to be as compatible as possible with old function `download` .
*usage:* download_file path_to_ile prosed_filename
*alias*: download
*Note:* You must use `download_file` to download `URLS[...]` or `SERVICE[NEWPHOTO]` URLs from Telegram server.
*example:*
```bash
########
# download from Telegram server
# photo received in a chat
photo="${URLS[PHOTO]}")"
echo "$photo" -> photo/file_1234.jpg
# first download
file="$(download_file "${photo}"
echo "$file" -> ./data-bot-bash/photo-file_1234.jpg
# second download
file="$(download_file "${photo}"
echo "$file" -> ./data-bot-bash/jkdfhi-photo-file_1234.jpg
ls data-bot-bash/*.jpg
photo-file_1234.jpg jkdfhi-photo-file_1234.jpg
########
# download from other sources (full URL)
file="$(download "https://avatars.githubusercontent.com/u/13046303")"
echo "$file" -> ./data-bot-bash/download-askjgftGJGdh1Z
file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")"
echo "$file" -> ./data-bot-bash/avatar.jpg
file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")"
echo "$file" -> ./data-bot-bash/jhsdf-avatar.jpg
ls data-bot-bash/
avatar.jpg jhsdf-avatar.jpg download-askjgftGJGdh1Z
#######
# manually download files to current directory (not recommended)
getJson "${FILEURL}/${photo}" >"downloaded_photo.jpg"
getJson "https://avatars.githubusercontent.com/u/13046303" >"avatar.jpg"
ls -F
JSON.sh/ bin/ modules/ data-bot-bash/
avatar.jpg bashbot.sh* botconfig.jssh commands.sh count.jssh downloaded_photo.jpg mycommands.sh ...
```
##### get_file
`get_file` get the `path` to a file on Telegram server by it's `file_id` . File `path` is only valid for use with your bot token.
*usage:* url="$(get_file "file_id")"
*example*:
```bash
# download file by file_id
file_id="kjhdsfhkj-kjshfbsdbfkjhsdkfjn"
path="$(get_file "${file_id}")"
file="$(download_file "${path}")"
# one line
file="$(download_file "$(get_file "${file_id}")")"
```
2021-01-10 19:46:56 +00:00
---
2021-01-14 19:57:23 +00:00
### Manage Group
2021-01-10 19:46:56 +00:00
To use the following functions the bot must have administrator status in the chat / group
2021-01-27 18:34:08 +00:00
##### chat_member_count
`chat_member_count` returns (putput) number of chat members.
*usage:* num_members="$(chat_member_count "CHAT[ID]")"
2021-01-14 19:57:23 +00:00
##### set_chat_title
2021-01-10 19:46:56 +00:00
`set_chat_title` sets a new chat title. If new title is the same than current title Telegram return error 400
with description "Bad Request: chat title is not modified"
2021-01-25 19:34:50 +00:00
*usage:* set_chat_title "CHAT[ID]" "new chat title"
2021-01-10 19:46:56 +00:00
2021-01-14 19:57:23 +00:00
##### set_chat_description
2021-01-10 19:46:56 +00:00
`set_chat_description` sets a new description title. If new description is the same than current description Telegram return error 400
with description "Bad Request: chat description is not modified"
2021-01-25 19:34:50 +00:00
*usage:* set_chat_description "CHAT[ID]" "new chat description"
2021-01-10 19:46:56 +00:00
2021-01-14 19:57:23 +00:00
##### new_chat_invite
`new_chat_invite` generate a new invite link for a chat; any previously generated link is revoked.
Returns the new invite link as String on success.
2021-01-25 19:34:50 +00:00
*usage:* new_chat_invite "CHAT[ID]"
2021-01-14 19:57:23 +00:00
##### delete_chat_photo
2021-01-25 19:34:50 +00:00
*usage:* delete_chat_photo "CHAT[ID]"
2021-01-14 19:57:23 +00:00
##### pin_chat_message
`pin_chat_message` add a message to the list of pinned messages in a chat.
2021-01-25 19:34:50 +00:00
*usage:* pin_chat_message "CHAT[ID]" "message_id"
2021-01-14 19:57:23 +00:00
##### unpin_chat_message
`unpin_chat_message` remove a message from the list of pinned messages in a chat.
2021-01-25 19:34:50 +00:00
*usage:* unpin_chat_message "CHAT[ID]" "message_id"
2021-01-14 19:57:23 +00:00
##### unpinall_chat_message
`unpinall_chat_message` clear the list of pinned messages in a chat.
2021-01-25 19:34:50 +00:00
*usage:* unpinall_chat_message "CHAT[ID]"
2021-01-14 19:57:23 +00:00
##### delete_chat_stickers
`delete_chat_stickers` deletes a group sticker set from a supergroup.
2021-01-25 19:34:50 +00:00
*usage:* delete_chat_stickers "CHAT[ID]"
2021-01-14 19:57:23 +00:00
2019-05-16 14:43:44 +00:00
----
### User Access Control
2019-06-13 15:02:40 +00:00
The following basic user control functions are part of the Telegram API.
More advanced API functions are currently not implemented in bashbot.
2019-05-16 14:43:44 +00:00
##### kick_chat_member
If your Bot is a chat admin he can kick and ban a user.
2021-01-25 19:34:50 +00:00
*usage:* kick_chat_member "CHAT[ID]" "USER[ID]"
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*alias:* _kick_user "USER[ID]"
2019-05-16 14:43:44 +00:00
##### unban_chat_member
2020-06-23 14:11:45 +00:00
If your Bot is a chat admin can unban a kicked user.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* unban_chat_member "CHAT[ID]" "USER[ID]"
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*alias:* _unban "USER[ID]"
2019-05-16 14:43:44 +00:00
##### leave_chat
Your Bot will leave the chat.
2021-01-25 19:34:50 +00:00
*usage:* leave_chat "CHAT[ID]"
2019-05-16 14:43:44 +00:00
*alias:* _leave
```bash
2020-12-29 10:02:28 +00:00
if bot_is_admin ; then
2019-05-16 14:43:44 +00:00
send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*"
leave_chat "${CHAT[ID]}"
fi
```
2020-12-29 10:02:28 +00:00
See also [kick Chat Member ](https://core.telegram.org/bots/api/#kickchatmember )*
2019-05-16 14:43:44 +00:00
2021-01-25 19:17:38 +00:00
##### promote_chat_member
2021-01-25 19:34:50 +00:00
`promote_chat_member` promote or denote user rights in a chat. Bot must be admin and can only promote/denote rights he owns.
2021-01-25 19:17:38 +00:00
Right are specified as "right:bool" pairs, where right is one of `long` or `short` listed below, followed
by `:true` or `:false` . Anything but `:true` (e.g. nothing or :xyz) is `:false` .
long: `is_anonymous can_change_info can_post_messages can_edit_messages can_delete_messages can_invite_users can_restrict_members can_pin_messages can_promote_member`
short: `anon change post edit delete invite restrict pin promote`
*usage:* promote_chat_member "CHAT[ID]" "USER[ID]" "right[:true|false]" ... "right[:true|false]"
See also [promote Chat Member ](https://core.telegram.org/bots/api#promotechatmember )*
*example:*
```bash
# USER can post, can't edit, can't delete, can't pin message, can invite users
promote_chat_member "CHAT[ID}" "USER[ID]" "post:true" "can_edit_message" "delete:false" "pin:xxx" "invite:true"
```
2019-05-16 14:43:44 +00:00
----
2019-06-13 15:02:40 +00:00
The following functions are bashbot only and not part of the Telegram API.
2020-12-03 13:07:39 +00:00
##### bot_is_admin
Return true (0) if bot is admin or creator of given chat.
2021-01-25 19:34:50 +00:00
*usage:* bot_is_admin "CHAT[ID]"
2020-12-03 13:07:39 +00:00
*example:*
```bash
if bot_is_admin "${CHAT[ID]}"; then
send_markdown_message "${CHAT[ID]}" "*I'm admin...*"
fi
```
2019-05-16 14:43:44 +00:00
##### user_is_botadmin
Return true (0) if user is admin of bot, user id if botadmin is read from file './botadmin'.
2021-01-25 19:34:50 +00:00
*usage:* user_is_botadmin "USER[ID]"
2019-05-16 14:43:44 +00:00
*alias:* _is_botadmin
*example:*
```bash
2020-12-03 13:07:39 +00:00
user_is_botadmin "${CHAT[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN* ."
2019-05-16 14:43:44 +00:00
```
##### user_is_creator
Return true (0) if user is creator of given chat or chat is a private chat.
2021-01-25 19:34:50 +00:00
*usage:* user_is_creator "CHAT[ID]" "USER[ID]"
2019-05-16 14:43:44 +00:00
*alias:* _is_creator
##### user_is_admin
Return true (0) if user is admin or creator of given chat.
2021-01-25 19:34:50 +00:00
*usage:* user_is_admin "CHAT[ID]" "USER[ID]"
2019-05-16 14:43:44 +00:00
*alias:* _is_admin
*example:*
```bash
2020-12-03 13:07:39 +00:00
if user_is_admin "${CHAT[ID]}" ; then
2019-05-16 14:43:44 +00:00
send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*"
leave_chat "${CHAT[ID]}"
fi
```
*See also [Chat Member ](https://core.telegram.org/bots/api/#chatmember )*
##### user_is_allowed
2021-01-14 19:23:18 +00:00
`uers_is_allowed` checks if: user id botadmin, user is group admin or user is allowed to execute action..
Allowed actions are configured as User Access Control rules, see [Advanced Usage ](3_advanced.md )
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* user_is_allowed "USER[ID]" "action" "CHAT[ID]"
2019-05-16 14:43:44 +00:00
*example:*
```bash
if ! user_is_allowed "${USER[ID]}" "start" "${CHAT[ID]}" ; then
send_normal_message "${CHAT[ID]}" "You are not allowed to start Bot."
fi
```
----
### Inline Queries - answer direct queries to bot
Inline Queries allows users to interact with your bot directly without sending extra commands.
As an answer to an inline query you can send back one or more results to the Telegram client.
The Telegram client will then show the results to the user and let him select one.
##### answer_inline_query
answer_inline_query is provided for backward compatibility with older versions of bashbot.
It send back only one response to an inline query.
*usage:* answer_inline_query "$i{QUERY[ID]}" "type" "type arg 1" ... "type arg n"
*example:* - see [Advanced Usage ](3_advanced.md#Inline-queries )
##### answer_inline_multi
2020-06-23 14:11:45 +00:00
anwser_inline_multi allows you to send back a list of responses. Responses must be separated by ','.
2019-05-16 14:43:44 +00:00
*usage:* answer_inline_multi "${iQUERY[ID]}" "res, res, ... res"
*example:*
```bash
# note the starting " and ending " !!
answer_inline_multi "${iQUERY[ID]}" "
$(inline_query_compose "1" "photo" "https://avatars0.githubusercontent.com/u/13046303") ,
...
$(inline_query_compose "n" "photo" "https://avatars1.githubusercontent.com/u/4593242")
"
```
2019-06-13 15:02:40 +00:00
##### inline_query_compose
2019-05-16 14:43:44 +00:00
inline_query_compose composes one response element to to send back.
*usage:* inline_query_compose ID type args ....
```
ID = unique ID for this response, 1-64 byte long
type = type of answer, e.g. article, photo, video, location ...
args = mandatory arguments in the order they are described in telegram documentation
```
Currently the following types and arguments are implemented (optional arguments in parenthesis)
```
2019-05-31 20:20:22 +00:00
"article"|"message" title message (parse_mode description)
2019-05-16 14:43:44 +00:00
2019-05-31 20:20:22 +00:00
"photo" photo_URL (thumb_URL title description caption parse_mode keyboard)
"gif" photo_URL (thumb_URL title caption parse_mode keyboard)
"mpeg4_gif" mpeg_URL (thumb_URL title caption parse_mode keyboard)
"video" video_URL mime_type thumb_URL title (caption parse_mode keyboard)
"audio" audio_URL title (caption parse_mode keyboard)
"voice" voice_URL title (caption parse_mode keyboard)
"document" title document_URL mime_type (caption description parse_mode)
2019-05-16 14:43:44 +00:00
"location" latitude longitude title
2020-06-23 14:35:50 +00:00
"venue" latitude longitude title (address foursquare)
2019-05-16 14:43:44 +00:00
"contact" phone first (last thumb)
2019-05-31 20:20:22 +00:00
"cached_photo" file (title description caption parse_mode keyboard)
"cached_gif" file (title caption parse_mode keyboard)
"cached_mpeg4_gif" file (title caption parse_mode keyboard)
"cached_sticker" file (keyboard)
"cached_document" title file (description caption description parse_mode keyboard)
"cached_video" file title (description caption description parse_mode keyboard)
"cached_voice" file title (caption parse_mode keyboard)
"cached_audio" file title (caption parse_mode keyboard)
2019-05-16 14:43:44 +00:00
```
see [InlineQueryResult for more information ](https://core.telegram.org/bots/api#inlinequeryresult ) about response types and their arguments.
----
### Background and Interactive jobs
2020-06-23 14:11:45 +00:00
Background functions and interactive jobs extends the bot functionality to not only react to user input. You can start scripts for interactive
2019-06-06 14:04:42 +00:00
chats and send messages based on time or other external events.
2019-05-16 14:43:44 +00:00
2019-05-19 15:56:24 +00:00
##### start_proc
2020-12-29 09:18:41 +00:00
`startproc` starts a script, the output of the script is sent to the user or chat, user input will be sent back to the script. see [Advanced Usage ](3_advanced.md#Interactive-Chats )
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* start_proc "CHAT[ID]" "script"
2019-05-19 15:56:24 +00:00
2019-05-20 08:50:51 +00:00
*alias:* startproc "script"
2019-05-16 14:43:44 +00:00
*example:*
```bash
startproc 'examples/calc.sh'
```
2019-05-20 08:50:51 +00:00
2019-05-19 15:56:24 +00:00
##### check_proc
2019-05-16 14:43:44 +00:00
Return true (0) if an interactive script is running in the chat.
2021-01-25 19:34:50 +00:00
*usage:* check_prog "CHAT[ID]"
2019-05-19 15:56:24 +00:00
*alias:* checkprog
2019-05-16 14:43:44 +00:00
*example:*
```bash
2019-05-20 08:50:51 +00:00
if ! check_proc "${CHAT[ID]}" ; then
2019-05-16 14:43:44 +00:00
startproc "examples/calc.sh"
else
send_normal_message "${CHAT[ID]}" "Calc already running ..."
fi
```
2019-05-19 15:56:24 +00:00
##### kill_proc
2019-05-16 14:43:44 +00:00
Kill the interactive script running in the chat
2021-01-25 19:34:50 +00:00
*usage:* kill_proc "CHAT[ID]"
2019-05-19 15:56:24 +00:00
*alias:* killproc
2019-05-16 14:43:44 +00:00
*example:*
```bash
2019-05-20 08:50:51 +00:00
if check_proc "${CHAT[ID]}" ; then
2019-05-16 14:43:44 +00:00
killproc & & send_message "${CHAT[ID]}" "Command canceled."
else
send_message "${CHAT[ID]}" "Command is not running."
fi
```
----
2019-05-19 15:56:24 +00:00
##### start_back
2020-06-23 14:11:45 +00:00
Starts a script as a background job and attaches a job name to it. All output from a background job is sent to the associated chat.
2019-05-16 14:43:44 +00:00
2020-06-11 07:23:03 +00:00
In contrast to interactive chats, background jobs do not receive user input and can run forever. In addition you can suspend and restart running jobs, e.g. after reboot.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* start_back "CHAT[ID]" "script" "jobname"
2019-05-19 15:56:24 +00:00
*alias:* background "script" "jobname"
2019-05-16 14:43:44 +00:00
*example:*
```bash
background "examples/notify.sh" "notify"
```
2019-05-19 15:56:24 +00:00
##### check_back
2019-05-16 14:43:44 +00:00
Return true (0) if an background job is active in the given chat.
2021-01-25 19:34:50 +00:00
*usage:* check_back "CHAT[ID]" "jobname"
2019-05-19 15:56:24 +00:00
*alias:* checkback "jobname"
2019-05-16 14:43:44 +00:00
*example:*
```bash
2019-05-20 08:50:51 +00:00
if ! checkback "notify" ; then
2019-05-16 14:43:44 +00:00
send_normal_message "${CHAT[ID]}" "Start notify"
background "examples/notify.sh" "notify"
else
send_normal_message "${CHAT[ID]}" "Process notify already running."
fi
```
2019-05-19 15:56:24 +00:00
##### kill_back
2021-01-25 19:34:50 +00:00
*usage:* kill_back "CHAT[ID]" "jobname"
2019-05-19 15:56:24 +00:00
*alias:* killback "jobname"
2019-05-16 14:43:44 +00:00
*example:*
```bash
checkback "notify"
if [ "$res" -eq 0 ] ; then
send_normal_message "${CHAT[ID]}" "Kill notify"
killback "notify"
else
send_normal_message "${CHAT[ID]}" "Process notify not run."
fi
```
----
2019-05-20 08:50:51 +00:00
##### send_interactive
2020-12-29 10:02:28 +00:00
`send_interactive` is used to forward messages to interactive jobs.
Usually a message is automatically forwarded from within `commands.sh` , but you can send messages yourself.
2019-05-16 14:43:44 +00:00
2021-01-25 19:34:50 +00:00
*usage:* send_interactive "CHAT[ID]" "message"
2019-05-16 14:43:44 +00:00
2019-06-13 15:02:40 +00:00
----
2019-06-06 14:04:42 +00:00
### jsshDB
2020-12-29 10:16:14 +00:00
Since output generated by `JSON.sh` is so easy to use in bash, bashbot uses the format for a simple keys/value file store also.
2019-06-06 14:04:42 +00:00
2020-05-27 20:28:09 +00:00
#### fast and slow operations
2019-05-30 16:02:57 +00:00
2020-12-29 10:22:49 +00:00
jsshDB files are flat text files containing key/value pairs in the `JSON.sh` format.
Key/value pairs appearing later in the file overwrites earlier key/value pairs, Bashbot use this behavior to implement "fast replace" file operations.
2020-05-27 08:57:25 +00:00
2020-12-29 10:16:14 +00:00
"fast functions" add a new key/value pair to the end of a file without deleting an existing one, this is fast but over time the file grows to infinity.
2020-05-27 08:57:25 +00:00
2020-12-29 10:16:14 +00:00
"slow functions" read the file, modify the key/value pairs in memory and write the whole file back, this is slower but removes duplicate keys from the file.
2020-05-27 08:57:25 +00:00
2020-12-29 10:16:14 +00:00
Fast functions:
2020-05-27 08:57:25 +00:00
```
2020-06-01 10:00:37 +00:00
jssh_insertKeyDB , jssh_addKeyDB , jssh_countKeyDB
2020-05-27 08:57:25 +00:00
```
2020-12-29 10:16:14 +00:00
Slow functions:
2020-05-27 08:57:25 +00:00
```
2020-06-05 20:24:33 +00:00
jssh_writeDB, jssh_updateDB , jssh_deleteKeyDB, jssh_clearDB
2020-05-27 08:57:25 +00:00
```
2021-01-03 10:27:09 +00:00
#### Key / Value
JsshBD use bash associative arrays to store key/value pairs in memory. Associative arrays must be created with `declare -A` before first use.
```bash
# create key / value array
decleare -A ARRAY
ARRAY["key"]="value"
ARRAY["key,subkey"]="value2"
```
For keys the following charatcsers are allowed: `a-z A-Z 0-9 _ .` , multiple keys must be separated by `,` .
Keys contaiing other characters will be discarded when written to a file.
```bash
ARRAY["abc"]="abc" # OK
ARRAY["abx###"]="abc" # works in bash but will not saved to file
# write to file will discard second value
jssh_writeDB "ARRAY" "file"
cat file.jssh
["abc"] "abc"
```
2020-05-27 08:57:25 +00:00
2021-01-03 21:39:36 +00:00
*Hint*: Try `tr -dc "[:alnum:],.\r\n"` to strip invalid characters from key.
```bash
# strip key containing invalid characters
KEY="123abcABC,.#?(< >123ÄÖ*%& §"
OK_KEY="$(tr -dc "[:alnum:],.\r\n" <<< "${KEY}")"
# show stripped key
printf "%s\n" "${OK_KEY}"
123abcABC,.123
```
2020-05-27 20:53:47 +00:00
#### File naming and locking
2020-05-27 08:57:25 +00:00
2020-12-29 10:16:14 +00:00
A jssh fileDB consists of two files and must reside inside `BASHBOT_ETC` or `BASHBOT_DATA` .
2020-05-27 08:57:25 +00:00
2020-12-29 10:27:22 +00:00
- `filename.jssh` is the file containing the key/value pairs in JSON.sh format.
2020-12-29 10:16:14 +00:00
- `filename.jssh.flock` is used to provide read/write locking with flock
2020-05-27 08:57:25 +00:00
2020-12-29 10:02:28 +00:00
Path names containing `..` or not located in `BASHBOT_ETC` or `BASHBOT_DATA` are refused by jsshDB functions with an error.
2020-05-27 08:57:25 +00:00
2020-12-29 10:27:22 +00:00
jsshDB functions use file locking if `flock is available, read/write operations are serialised to wait until
2020-12-29 10:02:28 +00:00
previous operations are finished, see "man flock". To avoid deadlocks bashbot use a timeout of 10s for write and 5s for read operations.
2020-05-15 15:45:23 +00:00
2020-12-29 10:16:14 +00:00
For every `jssh_...DB` function a `jsshj_...DB_async` function exists also. In case don't want locking, use `jssh_...DB_async` functions.
2019-05-30 16:02:57 +00:00
2020-05-27 20:53:47 +00:00
*Example:* for allowed file names:
2019-05-30 16:02:57 +00:00
```bash
2019-06-06 14:04:42 +00:00
# bashbot is installed in /usr/local/telegram-bot-bash, BASHBOT_ETC is not set.
2019-05-30 16:02:57 +00:00
"myfile" -> /usr/local/telegram-bot-bash/myfile.jssh
"addons/myfile" -> /usr/local/telegram-bot-bash/addons/myfile.jssh
2019-06-06 14:04:42 +00:00
"${DATADIR}/myfile" -> /usr/local/telegram-bot-bash/data-bot-bash/myfile.jssh
"/home/someuser/myfile" -> function returns false, nothing done.
2019-05-30 16:02:57 +00:00
```
2019-05-30 12:07:29 +00:00
##### jssh_newDB
2020-06-23 14:35:50 +00:00
Creates new empty jsshDB file if not exist.
2019-05-30 12:07:29 +00:00
*usage:* jssh_newDB "filename"
2020-06-05 20:24:33 +00:00
*usage:* jssh_newDB_async "filename"
##### jssh_clearDB
Delete all contents of jsshDB file.
*usage:* jssh_clearDB "filename"
*usage:* jssh_clearDB_async "filename"
2019-06-04 16:04:52 +00:00
##### jssh_checkDB
2020-06-01 10:00:37 +00:00
Check if DB name respects the rules mentioned above and print to STDOUT the real/final path to DB file.
2019-06-04 16:04:52 +00:00
Used internally by all jssh DB functions, but can also used to get the real filename for a jssh DB.
2020-06-01 10:00:37 +00:00
An error is returned and nothing is printed if the given filename is not valid
2019-06-04 16:04:52 +00:00
*usage:* jssh_checkDB "filename"
2020-06-05 20:24:33 +00:00
*usage:* jssh_checkDB_async "filename"
2019-06-06 14:04:42 +00:00
```bash
if file=$(jssh_checkDB somename); then
echo "Final filename is ${file}"
else
echo "Something wrong with somename"
fi
# somename = data-bot-bash/somevalues
Final filename is data-bot-bash/somevalues.jssh
2019-06-04 16:04:52 +00:00
2019-06-06 14:04:42 +00:00
# somename = /home/someuser/myfile
Something wrong with /home/someuser/myfile
# somename = data-bot-bash/../../../somevalues
Something wrong with data-bot-bash/../../../somevalues
```
2019-06-04 16:04:52 +00:00
2019-05-30 12:07:29 +00:00
##### jssh_writeDB
2020-12-29 10:02:28 +00:00
Write content of an ARRAY into jsshDB file. ARRAY name must be declared with `declare -A ARRAY` before calling writeDB.
2019-05-30 12:07:29 +00:00
"DB" file MUST exist or nothing is written.
2019-06-06 14:04:42 +00:00
Note: Existing content is overwritten.
2019-06-04 16:04:52 +00:00
2019-05-30 12:07:29 +00:00
*usage:* jssh_writeDB "ARRAY" "filename"
2020-05-15 15:45:23 +00:00
*usage:* jssh_writeDB_async "ARRAY" "filename"
2019-05-30 12:07:29 +00:00
*example:*
```bash
2020-06-23 14:35:50 +00:00
# Prepare array to store values
2020-05-06 12:11:50 +00:00
declare -A WRITEVALUES
2020-05-06 12:36:23 +00:00
WRITEVALUES["value1"]="example"
2020-05-06 13:00:30 +00:00
WRITEVALUES["value2"]="a value"
2020-05-06 12:36:23 +00:00
WRITEVALUES["whynot","subindex1"]="whynot A"
WRITEVALUES["whynot","subindex2"]="whynot B"
WRITEVALUES["whynot","subindex2","text"]="This is an example content for pseudo multidimensional bash array"
2019-05-30 12:07:29 +00:00
# create DB
2019-06-06 14:04:42 +00:00
jssh_newDB "${DATADIR:-.}/myvalues"
2019-05-30 12:07:29 +00:00
# write to file data-bot-bash/somevalues.jssh from array MYVALUES
2020-05-06 12:11:50 +00:00
jssh_writeDB "WRITEVALUES" "${DATADIR:-}/myvalues"
2019-05-30 12:07:29 +00:00
2020-06-23 14:35:50 +00:00
# show what's written
2020-05-06 12:11:50 +00:00
cat "${DATADIR:-}/myvalues.jssh"
2020-05-06 12:36:23 +00:00
["value1"] "example"
2020-05-06 13:00:30 +00:00
["value2"] "a value"
2020-05-06 12:36:23 +00:00
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
2020-05-14 22:21:40 +00:00
["whynot","subindex1"] "whynot A"
```
##### jssh_printDB
2020-12-29 10:02:28 +00:00
Print content of an ARRAY to STDOUT. ARRAY name must be declared with `declare -A ARRAY` before calling printDB..
2020-05-14 22:21:40 +00:00
*usage:* jssh_printDB "ARRAY"
*example:*
```bash
2020-06-23 14:35:50 +00:00
# Prepare array to store values
2020-05-14 22:21:40 +00:00
declare -A PRINTVALUES
# read file data-bot-bash/myvalues.jssh into array READVALUES
jssh_readDB "PRINTVALUES" "${DATADIR:-}/myvalues"
# print DB to stdout
jssh_printDB READVALUES
["value1"] "example"
["value2"] "a value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
2020-05-06 12:36:23 +00:00
["whynot","subindex1"] "whynot A"```
2019-05-30 12:07:29 +00:00
```
2019-06-04 16:04:52 +00:00
##### jssh_updateDB
2020-12-29 10:02:28 +00:00
Update/Add content of an ARRAY into a jsshDB file. ARRAY name must be declared with `declare -A ARRAY` before calling updateDB.
2019-06-04 16:04:52 +00:00
"DB" file MUST exist or nothing is written.
2019-06-06 14:04:42 +00:00
Note: Existing content not in ARRAY is kept in file.
2019-06-04 16:04:52 +00:00
*usage:* jssh_updateDB "ARRAY" "filename"
2020-05-15 15:45:23 +00:00
*usage:* jssh_updateDB_async "ARRAY" "filename"
2019-06-04 16:04:52 +00:00
*example:*
```bash
# continued example from writeDB
MYVALUES=()
MYVALUES["newvalue"]="this is new"
# update file data-bot-bash/somevalues.jssh from array MYVALUES
2019-06-06 14:04:42 +00:00
jssh_updateDB "MYVALUES" "${DATADIR:-.}/myvalues"
2019-06-04 16:04:52 +00:00
2020-06-23 14:35:50 +00:00
# show what's written
2019-05-30 12:07:29 +00:00
["value1"] "value1"
["loveit"] "value2"
["whynot"] "value3"
2019-06-04 16:04:52 +00:00
["newvalue"] "this is new"
# now writeDB
cat "$DBfile"
2019-06-06 14:04:42 +00:00
jssh_writeDB "MYVALUES" "${DATADIR:-.}/myvalues"
2019-06-04 16:04:52 +00:00
2020-06-23 14:35:50 +00:00
# show what's written, ups!
2019-06-04 16:04:52 +00:00
cat "$DBfile"
["newvalue"] "this is new"
```
2020-05-06 12:36:23 +00:00
##### jssh_readDB
2020-12-29 10:02:28 +00:00
Read content of a file in JSON.sh format into given ARRAY. ARRAY name must be declared with `declare -A ARRAY` upfront,
2020-05-06 12:36:23 +00:00
*usage:* jssh_readDB "ARRAY" "filename"
2020-05-15 15:45:23 +00:00
*usage:* jssh_readDB_async "ARRAY" "filename"
2020-06-23 14:11:45 +00:00
Note: readDB uses concurrent / shared locking from flock so multiple processes can read from file, as long no process is writing.
Maximum timeout for reading is 1s to not block readers.
2020-05-15 15:45:23 +00:00
2020-05-06 12:36:23 +00:00
*example:*
```bash
2020-06-23 14:35:50 +00:00
# Prepare array to read values
2020-05-06 12:36:23 +00:00
declare -A READVALUES
# read file data-bot-bash/myvalues.jssh into array READVALUES
jssh_readDB "READVALUES" "${DATADIR:-}/myvalues"
# sinple command to output values ONLY
printf "${READVALUES[*]}"
2020-05-14 22:21:40 +00:00
example a value This is an example content for pseudo multidimensional bash array whynot B whynot A
2020-05-06 12:36:23 +00:00
2020-05-14 22:21:40 +00:00
# print DB to stdout
jssh_printDB READVALUES
["value1"] "example"
["value2"] "a value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"
2020-05-06 13:00:30 +00:00
2020-06-23 14:35:50 +00:00
# access Array
2020-05-06 13:00:30 +00:00
echo "${READVALUES[vaule2]}"
a value
2020-05-06 12:36:23 +00:00
2020-05-06 13:00:30 +00:00
# change / add values
READVALUES["value2"]="this is a changed value"
echo "${READVALUES[vaule2]}"
this is a changed value
READVALUES["value3"]="new value"
READVALUES[whynot,subindex3]="new subindex value"
# new output
2020-05-14 22:21:40 +00:00
jssh_printDB READVALUES
["value1"] "example"
["value3"] "new value"
["value2"] "this is a changed value"
["whynot","subindex2","text"] "This is an example content for pseudo multidimensional bash array"
["whynot","subindex3"] "new subindex value"
["whynot","subindex2"] "whynot B"
["whynot","subindex1"] "whynot A"
2020-05-06 13:00:30 +00:00
```
2020-05-14 13:47:04 +00:00
2020-06-01 10:00:37 +00:00
##### jssh_insertKeyDB
2020-05-15 15:45:23 +00:00
Insert, update, append a key=value pair to a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
2020-06-01 10:00:37 +00:00
*usage:* jssh_insertKeyDB "key" "value" "filename"
*usage:* jssh_insertKeyDB_asnyc "key" "value" "filename"
2020-05-15 15:45:23 +00:00
2020-06-23 14:35:50 +00:00
*deprecated:* jssh_insertDB *was renamed in version 0.96 to* jssh_insertKeyDB
2020-05-15 15:45:23 +00:00
2020-06-01 10:00:37 +00:00
Note: inserKeytDB uses also excusive write locking, but with a maximum timeout of 2s. insertKeyDB is a "fast" operation, simply adding the value to the end of the file.
2020-05-15 15:45:23 +00:00
*example:*
```bash
2020-06-01 10:00:37 +00:00
jssh_insertKeyDB "newkey" "an other value" "${DATADIR:-.}/myvalues"
2020-05-15 15:45:23 +00:00
```
##### jssh_deleteKeyDB
2020-06-23 14:11:45 +00:00
Deleted a key=value pair from a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
2020-05-15 15:45:23 +00:00
*usage:* jssh_deleteKeyDB "key" "filename"
*usage:* jssh_deleteKeyDB_async "key" "filename"
*example:*
```bash
jssh_deleteKeyDB "delkey"" "${DATADIR:-.}/myvalues"
```
2020-05-19 17:41:54 +00:00
##### jssh_countKeyDB
Increase a key=value pair from a jsshDB file by 1, key name is only allowed to contain '-a-zA-Z0-9,._'
If value is given key is increased by value.
2020-06-23 14:35:50 +00:00
Side effect: if value is given key is updated "in place" (slower) and file is cleaned up, if no value is given fast path is used
2020-05-19 17:48:38 +00:00
and new count is added to the end of file.
2020-05-19 17:41:54 +00:00
*usage:* jssh_countKeyDB "key" "filename" ["value"]
2020-05-19 17:48:38 +00:00
*usage:* jssh_countKeyDB_async "key" "filename" ["value"]
2020-05-19 17:41:54 +00:00
*example:*
```bash
jssh_countKeyDB "usercount"" "${DATADIR:-.}/myvalues"
```
2020-05-06 13:00:30 +00:00
https://linuxhint.com/associative_array_bash/
2020-05-15 15:45:23 +00:00
2020-05-06 13:00:30 +00:00
https://linuxconfig.org/how-to-use-arrays-in-bash-script
2020-05-06 12:36:23 +00:00
2021-01-31 09:00:15 +00:00
----
2021-01-31 09:19:33 +00:00
### Manage webhook
2021-01-31 09:29:38 +00:00
Bashbot default mode is to poll Telegram server for updates but Telegram offers also webhook as a more efficient method to deliver updates.
2021-01-31 09:19:33 +00:00
2021-02-01 11:58:57 +00:00
*Important*: Before enable webhook you must setup your server to [receive and process webhook updates from Telegram ](../examples/webhook )
I recommend to use webhook with a test bot first.
2021-01-31 09:00:15 +00:00
##### get_webhook_info
2021-01-31 09:19:33 +00:00
`get_webhook_info` get current status of webhook for your bot, e.g. url, waiting updates, last error.
2021-01-31 09:00:15 +00:00
*usage:* get_webhook_info
*example:*
```bash
bin/any_command.sh get_webhook_info
["URL"] ""
["OK"] "true"
["LASTERR"] ""
["COUNT"] "0"
["CERT"] "false"
["result","pending_update_count"] "0"
["ok"] "true"
["result","has_custom_certificate"] "false"
```
##### delete_webhook
2021-01-31 09:29:38 +00:00
`delete_webhook` deletes your bots current webhook, deletes outstanding updates also if second arg is `true`
2021-01-31 09:00:15 +00:00
*usage:* delete_webhook [true|false]
*example:*
```bash
bin/any_command.sh delete_webhook false
["RESULT"] "true"
["OK"] "true"
["result"] "true"
["ok"] "true"
["description"] "Webhook was deleted"
```
##### set_webhook
2021-01-31 11:38:40 +00:00
`set_webhook` instructs Telegram to use your bots webhook for delivering updates. If webhook is set
2021-01-31 09:19:33 +00:00
it's no more possible to pull updates from `bashbot start` , you must delete webhook first.
2021-01-31 09:29:38 +00:00
*Important*: Before using webhook you must setup your server to receive and process updates from Telegram!
2021-01-31 09:00:15 +00:00
*usage:* set_webhook "https://host.dom[:port][/path]" [max_conn]
2021-01-31 09:29:38 +00:00
First arg is webhook URL used to send updates to your bot, `:port` and `/path` are optional.
If `:port` is given it must be one of `:443` , `:80` , `:88` or `:8443` , default is`:80`.
2021-01-31 11:38:40 +00:00
For security reasons `BOTTOKEN` will be added to URL (_e.g. `https://myhost.com` -> `https://myhost.com/12345678:azndfhbgdfbbbdsfg/` _).
2021-01-31 09:29:38 +00:00
2021-01-31 09:00:15 +00:00
Second arg is max connection rate in the range 1-100, bashbot default is 1.
*example:*
```bash
2021-01-31 09:29:38 +00:00
bin/any_command.sh set_webhook "https://myhost.com/telegram" "2"
2021-01-31 09:00:15 +00:00
["OK"] "true"
["RESULT"] "true"
["ok"] "true"
["result"] "true"
["description"] "Webhook is set"
bin/any_command.sh get_webhook_info
["OK"] "true"
["URL"] "https://myhost.com/telegram/12345678:AABBCCDDEE...aabbccee124567890/"
["COUNT"] "0"
["CERT"] "false"
["ok"] "true"
["result","ip_address"] "1.2.3.4"
["result","url"] "https://myhost.com/telegram/12345678:AABBCCDDEE...aabbccee124567890/"
["result","pending_update_count"] "0"
["result","max_connections"] "2"
["result","has_custom_certificate"] "false"
```
2020-05-14 13:47:04 +00:00
----
2020-06-23 14:11:45 +00:00
### Aliases - shortcuts for often used functions
2020-12-29 10:02:28 +00:00
Aliases are handy shortcuts for use in `mycommands.sh` *only* , they avoid error prone typing of "${CHAT[ID]}" "${USER[ID]}" as much as possible.
Do not use them in other files e.g. `bashbot.sh` , modules, addons etc.
2019-05-16 14:43:44 +00:00
##### _is_botadmin
*usage:* _is_botadmin
*alias for:* user_is_botadmin "${USER[ID]}"
##### _is_admin
*usage:* _is_admin
*alias for:* user_is_admin "${CHAT[ID]}" "${USER[ID]}"
##### _is_allowed
*usage:* _is_allowed "what"
*alias for:* user_is_allowed "${USER[ID]}" "what" "${CHAT[ID]}"
----
##### _kick_user
2021-01-25 19:34:50 +00:00
*usage:* _kick_user "USER[ID]"
2019-05-16 14:43:44 +00:00
*alias for:* kick_chat_member "${CHAT[ID]}" "${USER[ID]}"
##### _unban
2021-01-25 19:34:50 +00:00
*usage:* _unban "USER[ID]"
2019-05-16 14:43:44 +00:00
*alias for:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}"
##### _leave
*usage:* _leave
*alias for:* leave_chat "${CHAT[ID]}"
----
##### _message
*usage:* _message "message"
*alias for:* send_normal_message "${CHAT[ID]}" "message"
##### _normal_message
*usage:* _normal_message "message"
*alias for:* send_normal_message "${CHAT[ID]}" "message"
##### _html_message
*usage:* _html_message "message"
*alias for:* send_html_message "${CHAT[ID]}" "message"
##### _markdown_message
*usage:* _markdown_message "message"
*alias for:* send_markdown_message "${CHAT[ID]}" "message"
----
#### _keyboard_numpad
*usage:* _keyboard_numpad
*alias for:* send_keyboard "${CHAT[ID]}" "" '["1","2","3"],["4","5","6"],["7","8","9"],["-","0","."]' "yes"
#### _keyboard_yesno
*usage:* _keyboard_yesno
*alias for:* send_keyboard '["yes","no"]'
#### _del_keyboard
*usage:* _del_keyboard
*alias for:* remove_keyboard "${CHAT[ID]}" ""
2019-06-13 15:02:40 +00:00
----
2019-05-16 14:43:44 +00:00
### Helper functions
2019-05-30 10:47:13 +00:00
##### _exec_if_function
Returns true, even if the given function does not exist. Return false if function exist but returns false.
*usage:* _exec_if_function function
*example:*
```bash
_exec_if_function "answer_inline_query" "${iQUERY[ID]}" "Answer params"
2020-06-23 14:35:50 +00:00
# fast replacement for module functions exists check:
2019-05-30 10:47:13 +00:00
if _is_function "answer_inline_query"
then
"answer_inline_query" "${iQUERY[ID]}" "Answer params"
fi
```
2019-05-19 15:56:24 +00:00
##### _exists
Returns true if the given function exist, can be used to check if a module is loaded.
*usage* _exists command
2019-05-30 10:47:13 +00:00
2019-05-19 15:56:24 +00:00
*example:*
```bash
_exists "curl" & & _message "Command curl is not installed!"
```
2019-05-16 14:43:44 +00:00
##### _is_function
Returns true if the given function exist, can be used to check if a module is loaded.
*usage* _is_function function
*example:*
```bash
_is_function "background" & & _message "you can run background jobs!"
```
----
### Bashbot internal functions
These functions are for internal use only and must not used in your bot commands.
2019-05-19 15:56:24 +00:00
##### procname
2019-05-20 08:50:51 +00:00
Returns PrefixBotname_Postfix
2019-05-19 15:56:24 +00:00
*usage:* procname postfix prefix
2019-05-21 08:53:52 +00:00
*example:*
```bash
# returns botname, if already set
procname
2020-06-23 14:35:50 +00:00
# returns unique identifier for everything related to chat
2019-05-21 08:53:52 +00:00
procname "${CHAT[ID]}"
# returns unique identifier for job, regardless of chat
procname "" "back-jobname-"
# returns unique identifier for a job related to a chat
# e.g. fifo, cmd and logfile name
procname "${CHAT[ID]}" "back-jobname-"
```
2019-05-19 15:56:24 +00:00
##### proclist
2019-05-21 08:53:52 +00:00
Returns process IDs of current bot processes containing string 'pattern' in name or argument.
*usage:* proclist pattern
*example:*
```bash
# list PIDs of all background processes
proclist "back-"
# list PIDs of all processes of a job
proclist "back-jobname-"
# list PIDs of all processes for a chat
proclist "_${CHAT[ID]}"
# list PIDs of all bot processes
proclist
```
##### killallproc
kill all current bot processes containing string 'pattern' in name or argument
2019-05-19 15:56:24 +00:00
2019-05-21 08:53:52 +00:00
*usage:* killallproc pattern
2019-05-19 15:56:24 +00:00
2019-05-21 08:53:52 +00:00
*example:*
```bash
# kill all background processes
killallproc "back-"
# kill all processes for a chat
killallproc "_${CHAT[ID]}"
# kill all bot processes, including YOURSELF!
killallproc
```
2019-05-16 14:43:44 +00:00
----
##### JsonDecode
Outputs decoded string to STDOUT
*usage:* JsonDecode "string"
2019-05-30 10:47:13 +00:00
##### Json2Array
2020-06-23 14:11:45 +00:00
Read JSON.sh style data from STDIN and assign to given ARRAY
2020-12-29 10:02:28 +00:00
ARRAY name must be declared with `declare -A ARRAY` before calling
2019-05-30 10:47:13 +00:00
*usage:* Json2Array "ARRAY"
##### Array2Json
Output ARRAY as JSON.sh style data to STDOUT
*usage:* Array2Json "ARRAY"
2019-05-16 14:43:44 +00:00
----
##### get_chat_member_status
2021-01-25 19:34:50 +00:00
*usage:* get_chat_member_status "CHAT[ID]" "USER[ID]"
2019-05-16 14:43:44 +00:00
----
##### process_client
2020-06-23 14:11:45 +00:00
Every Message sent to your Bot is processed by this function. It parse the send JSON and assign the found Values to bash variables.
2019-05-16 14:43:44 +00:00
##### process_updates
2020-06-23 14:11:45 +00:00
If new updates are available, this functions gets the JSON from Telegram and dispatch it.
2019-05-16 14:43:44 +00:00
2019-05-30 10:47:13 +00:00
##### process_inline
2020-06-23 14:11:45 +00:00
Every Inline Message sent to your Bot is processed by this function. It parse the send JSON and assign the found Values to bash variables.
2019-05-30 10:47:13 +00:00
##### start_timer
Start the the every minute timer ...
##### event_timer
2019-05-30 18:59:17 +00:00
Dispatcher for BASHBOT_EVENT_TIMER
2019-05-30 10:47:13 +00:00
##### event_timer
2019-05-30 18:59:17 +00:00
Dispatcher for BASHBOT_EVENT_INLINE
2019-05-30 10:47:13 +00:00
##### event_timer
2019-05-30 18:59:17 +00:00
Dispatcher for BASHBOT_EVENT_MESSAGE and related
2019-05-30 10:47:13 +00:00
2019-05-16 14:43:44 +00:00
----
2019-05-30 10:47:13 +00:00
2019-05-16 14:43:44 +00:00
##### getBotName
2020-06-23 14:11:45 +00:00
The name of your bot is available as bash variable "$ME", there is no need to call this function if Bot is running.
2019-05-16 14:43:44 +00:00
2019-05-30 10:47:13 +00:00
*usage:* ME="$(getBotName)"
2019-05-16 14:43:44 +00:00
2021-01-26 19:53:08 +00:00
2019-05-16 14:43:44 +00:00
#### [Prev Best Practice](5_practice.md)
#### [Next Notes for Developers](7_develop.md)
2021-02-06 16:38:37 +00:00
#### $$VERSION$$ v1.41-dev-9-g5294d00
2019-05-16 14:43:44 +00:00