resolve conflict with develop

This commit is contained in:
Kay Marquardt (Gnadelwartz) 2020-06-24 19:01:30 +02:00
commit d904371fb9
50 changed files with 915 additions and 691 deletions

View File

@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Enter text / Run command '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Bashbot (please complete the following information):**
- Version [ grep 'VERSION' bashbot.sh ]
- OS: [ uname -a]
- Shell [ bash --version]
**Additional context**
Add any other context about the problem here.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,17 +0,0 @@
---
name: Others
about: Anything else not a Bug or Feature
title: ''
labels: ''
assignees: ''
---
**Is your issue is related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe your idea or complaint**
A clear and concise description of what you want to talk about.
**Additional context**
Add any other context or screenshots about your idea or complaint here.

6
.gitignore vendored
View File

@ -2,7 +2,11 @@
/.github/ /.github/
/count* /count*
/token* /token*
/bocked* /blocked*
/botconf*
/botacl*
/botown*
ˆ.jssh
*.save *.save
*.log *.log
*.swp *.swp

View File

@ -91,9 +91,9 @@ Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadel
<p>Released to the public domain wherever applicable. Elsewhere, consider it released under the <a href="http://www.wtfpl.net/txt/copying/">WTFPLv2</a>.</p> <p>Released to the public domain wherever applicable. Elsewhere, consider it released under the <a href="http://www.wtfpl.net/txt/copying/">WTFPLv2</a>.</p>
<h2>Prerequisites</h2> <h2>Prerequisites</h2>
<p>Uses <a href="http://github.com/dominictarr/JSON.sh">JSON.sh</a>, but no more TMUX.</p> <p>Uses <a href="http://github.com/dominictarr/JSON.sh">JSON.sh</a>, but no more TMUX.</p>
<p>Even bashbot is written in bash, it depends on commands typically availible in a Unix/Linux Environment. More concret on the common commands provided by recent versions of <a href="https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands">coreutils</a>, <a href="https://en.wikipedia.org/wiki/BusyBox#Commands">busybox</a> or <a href="https://landley.net/toybox/help.html">toybox</a>, see <a href="doc/7_develop.md#common-commands">Developer Notes</a></p> <p>Even bashbot is written in bash, it depends on commands typically available in a Unix/Linux Environment. More concret on the common commands provided by recent versions of <a href="https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands">coreutils</a>, <a href="https://en.wikipedia.org/wiki/BusyBox#Commands">busybox</a> or <a href="https://landley.net/toybox/help.html">toybox</a>, see <a href="doc/7_develop.md#common-commands">Developer Notes</a></p>
<p><em>Note for MacOS and BSD Users:</em> As bashbot heavily uses modern bash and (gnu) grep/sed features, bashbot will not run without installing additional software, see <a href="doc/0_install.md">Install Bashbot</a></p> <p><em>Note for MacOS and BSD Users:</em> As bashbot heavily uses modern bash and (gnu) grep/sed features, bashbot will not run without installing additional software, see <a href="doc/0_install.md">Install Bashbot</a></p>
<p>Bashbot <a href="https://github.com/topkecleon/telegram-bot-bash">Documentation</a> and <a href="https://github.com/topkecleon/telegram-bot-bash/releases">Downloads</a> are availible on www.github.com</p> <p>Bashbot <a href="https://github.com/topkecleon/telegram-bot-bash">Documentation</a> and <a href="https://github.com/topkecleon/telegram-bot-bash/releases">Downloads</a> are available on www.github.com</p>
<h2>Documentation</h2> <h2>Documentation</h2>
<ul> <ul>
<li><a href="https://core.telegram.org/bots">Introdution to Telegram Bots</a></li> <li><a href="https://core.telegram.org/bots">Introdution to Telegram Bots</a></li>
@ -108,7 +108,7 @@ Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadel
<li><a href="doc/2_usage.md">Getting Started</a> <li><a href="doc/2_usage.md">Getting Started</a>
<ul> <ul>
<li>Managing your Bot</li> <li>Managing your Bot</li>
<li>Recieve data</li> <li>Receive data</li>
<li>Send messages</li> <li>Send messages</li>
<li>Send files, locations, keyboards</li> <li>Send files, locations, keyboards</li>
</ul></li> </ul></li>
@ -124,7 +124,7 @@ Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadel
<ul> <ul>
<li>Handling UTF-8 character sets</li> <li>Handling UTF-8 character sets</li>
<li>Run as other user or system service</li> <li>Run as other user or system service</li>
<li>Scedule bashbot from Cron</li> <li>Schedule bashbot from Cron</li>
<li>Use from CLI and Scripts</li> <li>Use from CLI and Scripts</li>
<li>Customize Bashbot Environment</li> <li>Customize Bashbot Environment</li>
</ul></li> </ul></li>
@ -132,7 +132,7 @@ Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadel
<ul> <ul>
<li>Customize mycommands.sh</li> <li>Customize mycommands.sh</li>
<li>Overwrite/disable commands</li> <li>Overwrite/disable commands</li>
<li>Seperate logic from commands</li> <li>Separate logic from commands</li>
<li>Test your Bot with shellcheck</li> <li>Test your Bot with shellcheck</li>
</ul></li> </ul></li>
<li><a href="doc/6_reference.md">Function Reference</a> <li><a href="doc/6_reference.md">Function Reference</a>
@ -143,24 +143,24 @@ Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadel
<li>jsshDB Bashbot key-value storage</li> <li>jsshDB Bashbot key-value storage</li>
<li>Background and Interactive Jobs</li> <li>Background and Interactive Jobs</li>
</ul></li> </ul></li>
<li><a href="doc/7_develop.md">Deveoper Notes</a> <li><a href="doc/7_develop.md">Developer Notes</a>
<ul> <ul>
<li>Debug bashbot</li> <li>Debug bashbot</li>
<li>Modules, addons, events</li> <li>Modules, addons, events</li>
<li>Setup your environment</li> <li>Setup your environment</li>
<li>Bashbot testsuite</li> <li>Bashbot test suite</li>
</ul></li> </ul></li>
<li><a href="examples/README.md">Examples Dir</a></li> <li><a href="examples/README.md">Examples Dir</a></li>
</ul> </ul>
<h3>Your really first bashbot in a nutshell</h3> <h3>Your really first bashbot in a nutshell</h3>
<p>To install and run bashbot you need acess to a linux/unix command line. If you don't know how to get accces to a linux/unix/bsd like command line you should stop reading here :-(</p> <p>To install and run bashbot you need access to a linux/unix command line. If you don't know how to get access to a linux/unix/bsd like command line you should stop reading here :-(</p>
<p>In addition you need a <a href="https://telegram.org">Telegram client</a> and a mobile phone to <a href="https://telegramguide.com/create-a-telegram-account/">register an account</a>. If you don't want to register for Telegram you should stop reading here ;-)</p> <p>In addition you need a <a href="https://telegram.org">Telegram client</a> and a mobile phone to <a href="https://telegramguide.com/create-a-telegram-account/">register an account</a>. If you don't want to register for Telegram you should stop reading here ;-)</p>
<p>After you're registered to Telegram send a message to <a href="https://telegram.me/botfather">@botfather</a>, <a href="doc/1_firstbot.md">create a new Telegram Bot token</a> and write it down. You need the token to install the bot.</p> <p>After you're registered to Telegram send a message to <a href="https://telegram.me/botfather">@botfather</a>, <a href="doc/1_firstbot.md">create a new Telegram Bot token</a> and write it down. You need the token to install the bot.</p>
<p>Now open a linux/unix/bsd terminal and check if bash is installed: <code>which bash &amp;&amp; echo "bash installed!"</code>. If you get an error message bash is not installed.</p> <p>Now open a linux/unix/bsd terminal and check if bash is installed: <code>which bash &amp;&amp; echo "bash installed!"</code>. If you get an error message bash is not installed.</p>
<p>Create a new directory and change to it: <code>mkdir tbb; cd tbb</code> and download the latest '*.tar.gz' file from <a href="https://github.com/topkecleon/telegram-bot-bash/releases">https://github.com/topkecleon/telegram-bot-bash/releases</a>. This can be done with the commands:</p> <p>Create a new directory and change to it: <code>mkdir tbb; cd tbb</code> and download the latest '*.tar.gz' file from <a href="https://github.com/topkecleon/telegram-bot-bash/releases">https://github.com/topkecleon/telegram-bot-bash/releases</a>. This can be done with the commands:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb1-1" title="1"><span class="fu">wget</span> -q https://github.com/<span class="va">$(</span><span class="fu">wget</span> -q https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - <span class="kw">|</span> <span class="fu">egrep</span> <span class="st">&#39;/.*/.*/.*tar.gz&#39;</span> -o<span class="va">)</span></a></code></pre></div> <div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb1-1" title="1"><span class="fu">wget</span> -q https://github.com/<span class="va">$(</span><span class="fu">wget</span> -q https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - <span class="kw">|</span> <span class="fu">egrep</span> <span class="st">&#39;/.*/.*/.*tar.gz&#39;</span> -o<span class="va">)</span></a></code></pre></div>
<p>Extract the '*.tar.gz' file and change to bashbot directory: <code>tar -xzf *.tar.gz; cd telegram-bot-bash</code>, install bashbot: <code>./bashbot.sh init</code> and enter your bot token when asked. All other questions can be answered by hitting the &lt;Return&gt; key.</p> <p>Extract the '*.tar.gz' file and change to bashbot directory: <code>tar -xzf *.tar.gz; cd telegram-bot-bash</code>, install bashbot: <code>./bashbot.sh init</code> and enter your bot token when asked. All other questions can be answered by hitting the &lt;Return&gt; key.</p>
<p>Thats all, now you can start your bot with <code>./bashbot.sh start</code> and send him messages:</p> <p>That's all, now you can start your bot with <code>./bashbot.sh start</code> and send him messages:</p>
<pre><code>/start <pre><code>/start
You are Botadmin You are Botadmin
@ -175,46 +175,57 @@ his is bashbot, the Telegram bot written entirely in bash.
It features background tasks and interactive chats, and can serve as an interface for CLI programs. It features background tasks and interactive chats, and can serve as an interface for CLI programs.
</code></pre> </code></pre>
<p>For more Information on how to install, customize and use your new bot, read the <a href="#Documentation">Documentation</a></p> <p>For more Information on how to install, customize and use your new bot, read the <a href="#Documentation">Documentation</a></p>
<h3>Log files</h3>
<p>Since version 0.96 bashbot log commands received/send and connection errors. If you start bashbot in debug mode bash stdout, stderr and all send/received telegram message are logged also.</p>
<p>To enable debug mode start bashbot with debug as third argument: <code>bashbot start debug</code></p>
<pre><code>├── logs
│   ├── BASHBOT.log # log what your bot is doing ...
│   ├── ERROR.log # connection errors from / to telegram API
│   │
│   ├── DEBUG.log # stdout/stderr of you bot (debug mode enabled)
│   └── MESSAGE.log # full text of all message send/received (debug mode enabled)
</code></pre>
<hr /> <hr />
<h2>Security Considerations</h2> <h2>Security Considerations</h2>
<p>Running a Telegram Bot means it is connected to the public and you never know whats send to your Bot.</p> <p>Running a Telegram Bot means it is connected to the public and you never know what's send to your Bot.</p>
<p>Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see <a href="https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells">Implications of wrong quoting</a></p> <p>Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see <a href="https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells">Implications of wrong quoting</a></p>
<p>Whenever you are processing input from from untrusted sources (messages, files, network) you must be as carefull as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everthing. In addition delete unused scripts and examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable all not used commands.</p> <p>Whenever you are processing input from from untrusted sources (messages, files, network) you must be as careful as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everything. In addition delete unused scripts and examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable all not used commands.</p>
<p><strong>Note:</strong> Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version! One of the most powerful features of unix shells like bash is variable and command substitution, this can lead to RCE and information disclosing bugs if you do not escape '$' porperly, see <a href="https://github.com/topkecleon/telegram-bot-bash/issues/125">Issue #125</a></p> <p><strong>Note:</strong> Until v0.941 (mai/22/2020) telegram-bot-bash had a remote code execution (RCE) bug, pls update if you use an older version! see <a href="https://github.com/topkecleon/telegram-bot-bash/issues/125">Issue #125</a></p>
<p>One of the most powerful features of unix shells like bash is variable and command substitution using <code>${}</code> and <code>$()</code>, but as they are expanded in double quotes, this can lead to RCE and information disclosing bugs in complex scripts like bashbot even bash does much to avoid this. So it's more secure to escape or remove '$' in input from user, files or network.</p>
<p>A powerful tool to improve your scripts is <code>shellcheck</code>. You can <a href="https://www.shellcheck.net/">use it online</a> or <a href="https://github.com/koalaman/shellcheck#installing">install shellcheck locally</a>. Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it's not allowed to push changes without passing all shellcheck tests. In addition bashbot has a <a href="doc/7_develop.md">test suite</a> to check if important functionality is working as expected.</p> <p>A powerful tool to improve your scripts is <code>shellcheck</code>. You can <a href="https://www.shellcheck.net/">use it online</a> or <a href="https://github.com/koalaman/shellcheck#installing">install shellcheck locally</a>. Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it's not allowed to push changes without passing all shellcheck tests. In addition bashbot has a <a href="doc/7_develop.md">test suite</a> to check if important functionality is working as expected.</p>
<h3>use printf whenever possible</h3> <h3>use printf whenever possible</h3>
<p>If you're writing a script and it is taking external input (from the user as arguments, or file names from the file system...), you shouldn't use echo to display it. <a href="https://unix.stackexchange.com/a/6581">Use printf whenever possible</a></p> <p>If you're writing a script and it is taking external input (from the user as arguments, or file names from the file system...), you shouldn't use echo to display it. <a href="https://unix.stackexchange.com/a/6581">Use printf whenever possible</a></p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb3-1" title="1"> <span class="co"># very simple</span></a> <div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb4-1" title="1"> <span class="co"># very simple</span></a>
<a class="sourceLine" id="cb3-2" title="2"> <span class="bu">echo</span> <span class="st">&quot;text with variables. PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a> <a class="sourceLine" id="cb4-2" title="2"> <span class="bu">echo</span> <span class="st">&quot;text with variables. PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb3-3" title="3"> <span class="bu">printf</span> <span class="st">&#39;%s\n&#39;</span> <span class="st">&quot;text with variables. PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a> <a class="sourceLine" id="cb4-3" title="3"> <span class="bu">printf</span> <span class="st">&#39;%s\n&#39;</span> <span class="st">&quot;text with variables. PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb3-4" title="4"> <span class="ex">-</span><span class="op">&gt;</span> text with variables. PWD=/home/xxx</a> <a class="sourceLine" id="cb4-4" title="4"> <span class="ex">-</span><span class="op">&gt;</span> text with variables. PWD=/home/xxx</a>
<a class="sourceLine" id="cb3-5" title="5"></a> <a class="sourceLine" id="cb4-5" title="5"></a>
<a class="sourceLine" id="cb3-6" title="6"> <span class="co"># more advanced</span></a> <a class="sourceLine" id="cb4-6" title="6"> <span class="co"># more advanced</span></a>
<a class="sourceLine" id="cb3-7" title="7"> <span class="va">FLOAT=</span><span class="st">&quot;1.2346777892864&quot;</span> <span class="va">INTEGER=</span><span class="st">&quot;12345.123&quot;</span></a> <a class="sourceLine" id="cb4-7" title="7"> <span class="va">FLOAT=</span><span class="st">&quot;1.2346777892864&quot;</span> <span class="va">INTEGER=</span><span class="st">&quot;12345.123&quot;</span></a>
<a class="sourceLine" id="cb3-8" title="8"> <span class="bu">echo</span> <span class="st">&quot;text with variabeles. float=</span><span class="va">$FLOAT</span><span class="st">, integer=</span><span class="va">$INTEGER</span><span class="st">, PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a> <a class="sourceLine" id="cb4-8" title="8"> <span class="bu">echo</span> <span class="st">&quot;text with variabeles. float=</span><span class="va">$FLOAT</span><span class="st">, integer=</span><span class="va">$INTEGER</span><span class="st">, PWD=</span><span class="va">$PWD</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb3-9" title="9"> <span class="ex">-</span><span class="op">&gt;</span>text with variables. float=1.2346777892864, integer=12345.123, PWD=/home/xxx</a> <a class="sourceLine" id="cb4-9" title="9"> <span class="ex">-</span><span class="op">&gt;</span>text with variables. float=1.2346777892864, integer=12345.123, PWD=/home/xxx</a>
<a class="sourceLine" id="cb3-10" title="10"></a> <a class="sourceLine" id="cb4-10" title="10"></a>
<a class="sourceLine" id="cb3-11" title="11"> <span class="bu">printf</span> <span class="st">&quot;text with variables. float=%.2f, integer=%d, PWD=%s\n&quot;</span> <span class="st">&quot;&quot;</span> <span class="st">&quot;</span><span class="va">$INTEGER</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$PWD</span><span class="st">&quot;</span></a> <a class="sourceLine" id="cb4-11" title="11"> <span class="bu">printf</span> <span class="st">&quot;text with variables. float=%.2f, integer=%d, PWD=%s\n&quot;</span> <span class="st">&quot;&quot;</span> <span class="st">&quot;</span><span class="va">$INTEGER</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$PWD</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb3-12" title="12"> <span class="ex">-</span><span class="op">&gt;</span>text with variables. float=1.23, integer=12345, PWD=/home/xxx</a></code></pre></div> <a class="sourceLine" id="cb4-12" title="12"> <span class="ex">-</span><span class="op">&gt;</span>text with variables. float=1.23, integer=12345, PWD=/home/xxx</a></code></pre></div>
<h3>Do not use #!/usr/bin/env bash</h3> <h3>Do not use #!/usr/bin/env bash</h3>
<p><strong>We stay with /bin/bash shebang, because it's more save from security perspective.</strong></p> <p><strong>We stay with /bin/bash shebang, because it's more save from security perspective.</strong></p>
<p>Using a fixed path to the system provided bash makes it harder for attackers or users to place alternative versions of bash and avoids using a possibly broken, mangled or compromised bash executable.</p> <p>Using a fixed path to the system provided bash makes it harder for attackers or users to place alternative versions of bash and avoids using a possibly broken, mangled or compromised bash executable.</p>
<p>If you are a BSD / MacOS user or must to use an other bash location, see <a href="doc/0_install.md">Install Bashbot</a></p> <p>If you are a BSD / MacOS user or must to use an other bash location, see <a href="doc/0_install.md">Install Bashbot</a></p>
<h3>Run your Bot as a restricted user</h3> <h3>Run your Bot as a restricted user</h3>
<p><strong>I recommend to run your bot as a user, with almost no access rights.</strong> All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. For the same reason ervery file your Bot can read is in danger to be disclosed. Restict your Bots access rigths to the absolute minimum.</p> <p><strong>I recommend to run your bot as a user, with almost no access rights.</strong> All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. For the same reason every file your Bot can read is in danger to be disclosed. Restict your Bots access rights to the absolute minimum.</p>
<p><strong>Never run your Bot as root, this is the most dangerous you can do!</strong> Usually the user 'nobody' has almost no rights on Unix/Linux systems. See <a href="doc/4_expert.md">Expert use</a> on how to run your Bot as an other user.</p> <p><strong>Never run your Bot as root, this is the most dangerous you can do!</strong> Usually the user 'nobody' has almost no rights on Unix/Linux systems. See <a href="doc/4_expert.md">Expert use</a> on how to run your Bot as an other user.</p>
<h3>Secure your Bot installation</h3> <h3>Secure your Bot installation</h3>
<p><strong>Your Bot configuration must no be readable from other users.</strong> Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in!</p> <p><strong>Your Bot configuration must no be readable from other users.</strong> Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in!</p>
<p>Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in <code>token</code> must be protected against other users. No one exept you must have write access to the Bot files. The Bot must be restricted to have write access to <code>count</code> and <code>tmp-bot-bash</code> only, all other files must be write protected.</p> <p>Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in <code>token</code> must be protected against other users. No one except you must have write access to the Bot files. The Bot must be restricted to have write access to <code>count</code> and <code>tmp-bot-bash</code> only, all other files must be write protected.</p>
<p>To set access rights for your bashbot installation to a reasonable default run <code>sudo ./bashbot.sh init</code> after every update or change to your installation directory.</p> <p>To set access rights for your bashbot installation to a reasonable default run <code>sudo ./bashbot.sh init</code> after every update or change to your installation directory.</p>
<h2>FAQ</h2> <h2>FAQ</h2>
<h3>Is this Bot insecure?</h3> <h3>Is this Bot insecure?</h3>
<p>Bashbot is not more (in)secure as any other Bot written in any other language, we have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks ...</p> <p>Bashbot is not more (in)secure as any other Bot written in any other language, we have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks ...</p>
<p><strong>Note:</strong> Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version!</p> <p><strong>Note:</strong> Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version!</p>
<h3>Why Bash and not the much better xyz?</h3> <h3>Why Bash and not the much better xyz?</h3>
<p>Well, thats a damn good question ... may be because I'm an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side:</p> <p>Well, that's a damn good question ... may be because I'm an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side:</p>
<ul> <ul>
<li>bashbot will run everywhere where bash is availible, from ebedded linux to mainframe</li> <li>bashbot will run everywhere where bash is available, from embedded linux to mainframe</li>
<li>easy to integrate with other shell script, e.g. for sending system message / health status</li> <li>easy to integrate with other shell script, e.g. for sending system message / health status</li>
<li>no need to install or learn a new programming language, library or framework</li> <li>no need to install or learn a new programming language, library or framework</li>
<li>no database, not event driven, not OO ...</li> <li>no database, not event driven, not OO ...</li>
@ -223,29 +234,29 @@ It features background tasks and interactive chats, and can serve as an interfac
<p>At the beginning bashbot was simply the file <code>bashbot.sh</code> you can copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.</p> <p>At the beginning bashbot was simply the file <code>bashbot.sh</code> you can copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more.</p>
<p>Hey no Problem, if you are finished with your cool bot run <code>dev/make-standalone.sh</code> to create a stripped down Version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see <a href="doc/7_develop.md">Create a stripped down Version of your Bot</a></p> <p>Hey no Problem, if you are finished with your cool bot run <code>dev/make-standalone.sh</code> to create a stripped down Version of your bot containing only 'bashbot.sh' and 'commands.sh'! For more information see <a href="doc/7_develop.md">Create a stripped down Version of your Bot</a></p>
<h3>Can I send messages from CLI and scripts?</h3> <h3>Can I send messages from CLI and scripts?</h3>
<p>Of course, you can send messages from CLI and scripts, simply install bashbot as <a href="#Your-really-first-bashbot-in-a-nutshell">described here</a>, send the messsage '/start' to set yourself as botadmin and stop the bot with <code>./bashbot.sh kill</code>.</p> <p>Of course, you can send messages from CLI and scripts, simply install bashbot as <a href="#Your-really-first-bashbot-in-a-nutshell">described here</a>, send the message '/start' to set yourself as botadmin and stop the bot with <code>./bashbot.sh kill</code>.</p>
<p>Run the following commands in your bash shell or script while you are in the installation directory:</p> <p>Run the following commands in your bash shell or script while you are in the installation directory:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb4-1" title="1"><span class="co"># prepare bash / script to send commands</span></a> <div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb5-1" title="1"><span class="co"># prepare bash / script to send commands</span></a>
<a class="sourceLine" id="cb4-2" title="2"><span class="bu">export</span> <span class="va">BASHBOT_HOME=</span><span class="st">&quot;</span><span class="va">$(</span><span class="bu">pwd</span><span class="va">)</span><span class="st">&quot;</span></a> <a class="sourceLine" id="cb5-2" title="2"><span class="bu">export</span> <span class="va">BASHBOT_HOME=</span><span class="st">&quot;</span><span class="va">$(</span><span class="bu">pwd</span><span class="va">)</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb4-3" title="3"><span class="bu">source</span> ./bashbot.sh source</a> <a class="sourceLine" id="cb5-3" title="3"><span class="bu">source</span> ./bashbot.sh source</a>
<a class="sourceLine" id="cb4-4" title="4"></a> <a class="sourceLine" id="cb5-4" title="4"></a>
<a class="sourceLine" id="cb4-5" title="5"><span class="co"># send me a test message</span></a> <a class="sourceLine" id="cb5-5" title="5"><span class="co"># send me a test message</span></a>
<a class="sourceLine" id="cb4-6" title="6"><span class="ex">send_message</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">cat</span> <span class="st">&quot;</span><span class="va">$BOTADMIN</span><span class="st">&quot;</span><span class="va">)</span><span class="st">&quot;</span> <span class="st">&quot;test&quot;</span></a> <a class="sourceLine" id="cb5-6" title="6"><span class="ex">send_message</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">cat</span> <span class="st">&quot;</span><span class="va">$BOTADMIN</span><span class="st">&quot;</span><span class="va">)</span><span class="st">&quot;</span> <span class="st">&quot;test&quot;</span></a>
<a class="sourceLine" id="cb4-7" title="7"></a> <a class="sourceLine" id="cb5-7" title="7"></a>
<a class="sourceLine" id="cb4-8" title="8"><span class="co"># send me output of a system command</span></a> <a class="sourceLine" id="cb5-8" title="8"><span class="co"># send me output of a system command</span></a>
<a class="sourceLine" id="cb4-9" title="9"><span class="ex">send_message</span> <span class="st">&quot;</span><span class="op">$(&lt;</span><span class="st">&quot;</span><span class="va">$BOTADMIN</span><span class="st">&quot;</span><span class="op">)</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">df</span> -h<span class="va">)</span><span class="st">&quot;</span></a></code></pre></div> <a class="sourceLine" id="cb5-9" title="9"><span class="ex">send_message</span> <span class="st">&quot;</span><span class="op">$(&lt;</span><span class="st">&quot;</span><span class="va">$BOTADMIN</span><span class="st">&quot;</span><span class="op">)</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">df</span> -h<span class="va">)</span><span class="st">&quot;</span></a></code></pre></div>
<p>For more information see <a href="doc/8_custom.md">Expert Use</a></p> <p>For more information see <a href="doc/8_custom.md">Expert Use</a></p>
<h3>Why do I get "EXPECTED value GOT EOF" on start?</h3> <h3>Blocked by telegram?</h3>
<p>May be your IP is blocked by telegram. You can test this by running curl or wget manually:</p> <p>This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a tor proxy on your server you may uncomment the <code>BASHBOT_CURL_ARGS</code> line in 'mycommands.sh'</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb5-1" title="1"><span class="ex">curl</span> -m 10 https://api.telegram.org/bot</a> <p>You can test if younare blockeds by running curl or wget manually:</p>
<a class="sourceLine" id="cb5-2" title="2"><span class="co">#curl: (28) Connection timed out after 10001 milliseconds</span></a> <div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb6-1" title="1"><span class="ex">curl</span> -m 10 https://api.telegram.org/bot</a>
<a class="sourceLine" id="cb5-3" title="3"></a> <a class="sourceLine" id="cb6-2" title="2"><span class="co">#curl: (28) Connection timed out after 10001 milliseconds</span></a>
<a class="sourceLine" id="cb5-4" title="4"><span class="fu">wget</span> -t 1 -T 10 https://api.telegram.org/bot</a> <a class="sourceLine" id="cb6-3" title="3"></a>
<a class="sourceLine" id="cb5-5" title="5"><span class="co">#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out.</span></a></code></pre></div> <a class="sourceLine" id="cb6-4" title="4"><span class="fu">wget</span> -t 1 -T 10 https://api.telegram.org/bot</a>
<p>This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a socks or tor proxy on your server look for the <code>BASHBOT_CURL_ARGS</code> lines in 'mycommands.sh' as example.</p> <a class="sourceLine" id="cb6-5" title="5"><span class="co">#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out.</span></a></code></pre></div>
<p>@Gnadelwartz</p> <p>@Gnadelwartz</p>
<h2>That's it!</h2> <h2>That's it!</h2>
<p>If you feel that there's something missing or if you found a bug, feel free to submit a pull request!</p> <p>If you feel that there's something missing or if you found a bug, feel free to submit a pull request!</p>
<h4>$$VERSION$$ v0.96-0-g3871ca9</h4> <h4>$$VERSION$$ v0.98-dev-70-g694ee61</h4>
</body> </body>
</html> </html>

View File

@ -11,13 +11,13 @@ Elsewhere, consider it released under the [WTFPLv2](http://www.wtfpl.net/txt/cop
## Prerequisites ## Prerequisites
Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX. Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX.
Even bashbot is written in bash, it depends on commands typically availible in a Unix/Linux Environment. Even bashbot is written in bash, it depends on commands typically available in a Unix/Linux Environment.
More concret on the common commands provided by recent versions of [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or [toybox](https://landley.net/toybox/help.html), see [Developer Notes](doc/7_develop.md#common-commands) More concret on the common commands provided by recent versions of [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or [toybox](https://landley.net/toybox/help.html), see [Developer Notes](doc/7_develop.md#common-commands)
*Note for MacOS and BSD Users:* As bashbot heavily uses modern bash and (gnu) grep/sed features, bashbot will not run without installing additional software, see [Install Bashbot](doc/0_install.md) *Note for MacOS and BSD Users:* As bashbot heavily uses modern bash and (gnu) grep/sed features, bashbot will not run without installing additional software, see [Install Bashbot](doc/0_install.md)
Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are availible on www.github.com Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are available on www.github.com
## Documentation ## Documentation
* [Introdution to Telegram Bots](https://core.telegram.org/bots) * [Introdution to Telegram Bots](https://core.telegram.org/bots)
@ -29,7 +29,7 @@ Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Do
* [Get Bottoken from Botfather](doc/1_firstbot.md) * [Get Bottoken from Botfather](doc/1_firstbot.md)
* [Getting Started](doc/2_usage.md) * [Getting Started](doc/2_usage.md)
* Managing your Bot * Managing your Bot
* Recieve data * Receive data
* Send messages * Send messages
* Send files, locations, keyboards * Send files, locations, keyboards
* [Advanced Features](doc/3_advanced.md) * [Advanced Features](doc/3_advanced.md)
@ -41,13 +41,13 @@ Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Do
* [Expert Use](doc/4_expert.md) * [Expert Use](doc/4_expert.md)
* Handling UTF-8 character sets * Handling UTF-8 character sets
* Run as other user or system service * Run as other user or system service
* Scedule bashbot from Cron * Schedule bashbot from Cron
* Use from CLI and Scripts * Use from CLI and Scripts
* Customize Bashbot Environment * Customize Bashbot Environment
* [Best Practices](doc/5_practice.md) * [Best Practices](doc/5_practice.md)
* Customize mycommands.sh * Customize mycommands.sh
* Overwrite/disable commands * Overwrite/disable commands
* Seperate logic from commands * Separate logic from commands
* Test your Bot with shellcheck * Test your Bot with shellcheck
* [Function Reference](doc/6_reference.md) * [Function Reference](doc/6_reference.md)
* Sending Messages, Files, Keyboards * Sending Messages, Files, Keyboards
@ -55,16 +55,16 @@ Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Do
* Inline Queries * Inline Queries
* jsshDB Bashbot key-value storage * jsshDB Bashbot key-value storage
* Background and Interactive Jobs * Background and Interactive Jobs
* [Deveoper Notes](doc/7_develop.md) * [Developer Notes](doc/7_develop.md)
* Debug bashbot * Debug bashbot
* Modules, addons, events * Modules, addons, events
* Setup your environment * Setup your environment
* Bashbot testsuite * Bashbot test suite
* [Examples Dir](examples/README.md) * [Examples Dir](examples/README.md)
### Your really first bashbot in a nutshell ### Your really first bashbot in a nutshell
To install and run bashbot you need acess to a linux/unix command line. If you don't know how to get accces to a linux/unix/bsd like command line you should stop reading here :-( To install and run bashbot you need access to a linux/unix command line. If you don't know how to get access to a linux/unix/bsd like command line you should stop reading here :-(
In addition you need a [Telegram client](https://telegram.org) and a mobile phone to [register an account](https://telegramguide.com/create-a-telegram-account/). In addition you need a [Telegram client](https://telegram.org) and a mobile phone to [register an account](https://telegramguide.com/create-a-telegram-account/).
If you don't want to register for Telegram you should stop reading here ;-) If you don't want to register for Telegram you should stop reading here ;-)
@ -85,7 +85,7 @@ Extract the '*.tar.gz' file and change to bashbot directory: ```tar -xzf *.tar.g
install bashbot: ```./bashbot.sh init``` and enter your bot token when asked. All other questions can be answered install bashbot: ```./bashbot.sh init``` and enter your bot token when asked. All other questions can be answered
by hitting the \<Return\> key. by hitting the \<Return\> key.
Thats all, now you can start your bot with ```./bashbot.sh start``` and send him messages: That's all, now you can start your bot with ```./bashbot.sh start``` and send him messages:
``` ```
/start /start
@ -102,17 +102,37 @@ It features background tasks and interactive chats, and can serve as an interfac
``` ```
For more Information on how to install, customize and use your new bot, read the [Documentation](#Documentation) For more Information on how to install, customize and use your new bot, read the [Documentation](#Documentation)
### Log files
Since version 0.96 bashbot log commands received/send and connection errors. If you start bashbot in debug mode
bash stdout, stderr and all send/received telegram message are logged also.
To enable debug mode start bashbot with debug as third argument: `bashbot start debug`
```
├── logs
│   ├── BASHBOT.log # log what your bot is doing ...
│   ├── ERROR.log # connection errors from / to telegram API
│   │
│   ├── DEBUG.log # stdout/stderr of you bot (debug mode enabled)
│   └── MESSAGE.log # full text of all message send/received (debug mode enabled)
```
---- ----
## Security Considerations ## Security Considerations
Running a Telegram Bot means it is connected to the public and you never know whats send to your Bot. Running a Telegram Bot means it is connected to the public and you never know what's send to your Bot.
Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see [Implications of wrong quoting](https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells) Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see [Implications of wrong quoting](https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells)
Whenever you are processing input from from untrusted sources (messages, files, network) you must be as carefull as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everthing. In addition delete unused scripts and examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable all not used commands. Whenever you are processing input from from untrusted sources (messages, files, network) you must be as careful as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everything. In addition delete unused scripts and examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable all not used commands.
**Note:** Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version! **Note:** Until v0.941 (mai/22/2020) telegram-bot-bash had a remote code execution (RCE) bug, pls update if you use an older version!
One of the most powerful features of unix shells like bash is variable and command substitution, this can lead to RCE and information disclosing bugs if you do not escape '$' porperly, see [Issue #125](https://github.com/topkecleon/telegram-bot-bash/issues/125) see [Issue #125](https://github.com/topkecleon/telegram-bot-bash/issues/125)
One of the most powerful features of unix shells like bash is variable and command substitution using ```${}``` and ```$()```,
but as they are expanded in double quotes, this can lead to RCE and information disclosing bugs in complex scripts like bashbot
even bash does much to avoid this. So it's more secure to escape or remove '$' in input from user, files or network.
A powerful tool to improve your scripts is ```shellcheck```. You can [use it online](https://www.shellcheck.net/) or [install shellcheck locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it's not allowed to push changes without passing all shellcheck tests. A powerful tool to improve your scripts is ```shellcheck```. You can [use it online](https://www.shellcheck.net/) or [install shellcheck locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it's not allowed to push changes without passing all shellcheck tests.
In addition bashbot has a [test suite](doc/7_develop.md) to check if important functionality is working as expected. In addition bashbot has a [test suite](doc/7_develop.md) to check if important functionality is working as expected.
@ -149,14 +169,14 @@ If you are a BSD / MacOS user or must to use an other bash location, see [Insta
### Run your Bot as a restricted user ### Run your Bot as a restricted user
**I recommend to run your bot as a user, with almost no access rights.** **I recommend to run your bot as a user, with almost no access rights.**
All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked.
For the same reason ervery file your Bot can read is in danger to be disclosed. Restict your Bots access rigths to the absolute minimum. For the same reason every file your Bot can read is in danger to be disclosed. Restict your Bots access rights to the absolute minimum.
**Never run your Bot as root, this is the most dangerous you can do!** Usually the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert use](doc/4_expert.md) on how to run your Bot as an other user. **Never run your Bot as root, this is the most dangerous you can do!** Usually the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert use](doc/4_expert.md) on how to run your Bot as an other user.
### Secure your Bot installation ### Secure your Bot installation
**Your Bot configuration must no be readable from other users.** Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in! **Your Bot configuration must no be readable from other users.** Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in!
Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in ```token``` must be protected against other users. No one exept you must have write access to the Bot files. The Bot must be restricted to have write access to ```count``` and ```tmp-bot-bash``` only, all other files must be write protected. Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in ```token``` must be protected against other users. No one except you must have write access to the Bot files. The Bot must be restricted to have write access to ```count``` and ```tmp-bot-bash``` only, all other files must be write protected.
To set access rights for your bashbot installation to a reasonable default run ```sudo ./bashbot.sh init``` after every update or change to your installation directory. To set access rights for your bashbot installation to a reasonable default run ```sudo ./bashbot.sh init``` after every update or change to your installation directory.
@ -168,9 +188,9 @@ Bashbot is not more (in)secure as any other Bot written in any other language, w
**Note:** Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version! **Note:** Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code execution bug, pls update if you use an older version!
### Why Bash and not the much better xyz? ### Why Bash and not the much better xyz?
Well, thats a damn good question ... may be because I'm an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side: Well, that's a damn good question ... may be because I'm an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side:
- bashbot will run everywhere where bash is availible, from ebedded linux to mainframe - bashbot will run everywhere where bash is available, from embedded linux to mainframe
- easy to integrate with other shell script, e.g. for sending system message / health status - easy to integrate with other shell script, e.g. for sending system message / health status
- no need to install or learn a new programming language, library or framework - no need to install or learn a new programming language, library or framework
- no database, not event driven, not OO ... - no database, not event driven, not OO ...
@ -183,7 +203,7 @@ Hey no Problem, if you are finished with your cool bot run ```dev/make-standalon
### Can I send messages from CLI and scripts? ### Can I send messages from CLI and scripts?
Of course, you can send messages from CLI and scripts, simply install bashbot as [described here](#Your-really-first-bashbot-in-a-nutshell), Of course, you can send messages from CLI and scripts, simply install bashbot as [described here](#Your-really-first-bashbot-in-a-nutshell),
send the messsage '/start' to set yourself as botadmin and stop the bot with ```./bashbot.sh kill```. send the message '/start' to set yourself as botadmin and stop the bot with ```./bashbot.sh kill```.
Run the following commands in your bash shell or script while you are in the installation directory: Run the following commands in your bash shell or script while you are in the installation directory:
@ -201,8 +221,10 @@ send_message "$(<"$BOTADMIN")" "$(df -h)"
For more information see [Expert Use](doc/8_custom.md) For more information see [Expert Use](doc/8_custom.md)
### Why do I get "EXPECTED value GOT EOF" on start? ### Blocked by telegram?
May be your IP is blocked by telegram. You can test this by running curl or wget manually: This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a tor proxy on your server you may uncomment the ```BASHBOT_CURL_ARGS``` line in 'mycommands.sh'
You can test if younare blockeds by running curl or wget manually:
```bash ```bash
curl -m 10 https://api.telegram.org/bot curl -m 10 https://api.telegram.org/bot
#curl: (28) Connection timed out after 10001 milliseconds #curl: (28) Connection timed out after 10001 milliseconds
@ -210,8 +232,6 @@ curl -m 10 https://api.telegram.org/bot
wget -t 1 -T 10 https://api.telegram.org/bot wget -t 1 -T 10 https://api.telegram.org/bot
#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out. #Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out.
``` ```
This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a socks or tor proxy on your server look for the ```BASHBOT_CURL_ARGS``` lines in 'mycommands.sh' as example.
@Gnadelwartz @Gnadelwartz
@ -219,4 +239,4 @@ This may happen if to many wrong requests are sent to api.telegram.org, e.g. usi
If you feel that there's something missing or if you found a bug, feel free to submit a pull request! If you feel that there's something missing or if you found a bug, feel free to submit a pull request!
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -15,7 +15,7 @@ Elsewhere, consider it released under the
## Prerequisites ## Prerequisites
Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX. Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX.
Even bashbot is written in bash, it depends on commands typically availible in Even bashbot is written in bash, it depends on commands typically available in
a Unix/Linux Environment. a Unix/Linux Environment.
More concret on the common commands provided by recent versions of More concret on the common commands provided by recent versions of
[coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands),
@ -30,7 +30,7 @@ see [Install Bashbot](doc/0_install.md)
Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and
[Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are [Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are
availible on www.github.com available on www.github.com
## Documentation ## Documentation
* [Introdution to Telegram Bots](https://core.telegram.org/bots) * [Introdution to Telegram Bots](https://core.telegram.org/bots)
@ -42,7 +42,7 @@ availible on www.github.com
* [Get Bottoken from Botfather](doc/1_firstbot.md) * [Get Bottoken from Botfather](doc/1_firstbot.md)
* [Getting Started](doc/2_usage.md) * [Getting Started](doc/2_usage.md)
* Managing your Bot * Managing your Bot
* Recieve data * Receive data
* Send messages * Send messages
* Send files, locations, keyboards * Send files, locations, keyboards
* [Advanced Features](doc/3_advanced.md) * [Advanced Features](doc/3_advanced.md)
@ -54,13 +54,13 @@ availible on www.github.com
* [Expert Use](doc/4_expert.md) * [Expert Use](doc/4_expert.md)
* Handling UTF-8 character sets * Handling UTF-8 character sets
* Run as other user or system service * Run as other user or system service
* Scedule bashbot from Cron * Schedule bashbot from Cron
* Use from CLI and Scripts * Use from CLI and Scripts
* Customize Bashbot Environment * Customize Bashbot Environment
* [Best Practices](doc/5_practice.md) * [Best Practices](doc/5_practice.md)
* Customize mycommands.sh * Customize mycommands.sh
* Overwrite/disable commands * Overwrite/disable commands
* Seperate logic from commands * Separate logic from commands
* Test your Bot with shellcheck * Test your Bot with shellcheck
* [Function Reference](doc/6_reference.md) * [Function Reference](doc/6_reference.md)
* Sending Messages, Files, Keyboards * Sending Messages, Files, Keyboards
@ -68,17 +68,17 @@ availible on www.github.com
* Inline Queries * Inline Queries
* jsshDB Bashbot key-value storage * jsshDB Bashbot key-value storage
* Background and Interactive Jobs * Background and Interactive Jobs
* [Deveoper Notes](doc/7_develop.md) * [Developer Notes](doc/7_develop.md)
* Debug bashbot * Debug bashbot
* Modules, addons, events * Modules, addons, events
* Setup your environment * Setup your environment
* Bashbot testsuite * Bashbot test suite
* [Examples Dir](examples/README.md) * [Examples Dir](examples/README.md)
### Your really first bashbot in a nutshell ### Your really first bashbot in a nutshell
To install and run bashbot you need acess to a linux/unix command line. If you To install and run bashbot you need access to a linux/unix command line. If you
don't know how to get accces to a linux/unix/bsd like command line you should don't know how to get access to a linux/unix/bsd like command line you should
stop reading here :-( stop reading here :-(
In addition you need a [Telegram client](https://telegram.org) and a mobile In addition you need a [Telegram client](https://telegram.org) and a mobile
@ -111,7 +111,7 @@ install bashbot: ```./bashbot.sh init``` and enter your bot token when asked.
All other questions can be answered All other questions can be answered
by hitting the \<Return\> key. by hitting the \<Return\> key.
Thats all, now you can start your bot with ```./bashbot.sh start``` and send That's all, now you can start your bot with ```./bashbot.sh start``` and send
him messages: him messages:
``` ```
/start /start
@ -131,11 +131,30 @@ interface for CLI programs.
For more Information on how to install, customize and use your new bot, read For more Information on how to install, customize and use your new bot, read
the [Documentation](#Documentation) the [Documentation](#Documentation)
### Log files
Since version 0.96 bashbot log commands received/send and connection errors. If
you start bashbot in debug mode
bash stdout, stderr and all send/received telegram message are logged also.
To enable debug mode start bashbot with debug as third argument: `bashbot start
debug`
```
├── logs
│   ├── BASHBOT.log # log what your bot is doing ...
│   ├── ERROR.log # connection errors from / to telegram API
│   │
│   ├── DEBUG.log # stdout/stderr of you bot (debug mode enabled)
│   └── MESSAGE.log # full text of all message send/received (debug mode
enabled)
```
---- ----
## Security Considerations ## Security Considerations
Running a Telegram Bot means it is connected to the public and you never know Running a Telegram Bot means it is connected to the public and you never know
whats send to your Bot. what's send to your Bot.
Bash scripts in general are not designed to be bullet proof, so consider this Bash scripts in general are not designed to be bullet proof, so consider this
Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' Bot as a proof of concept. Bash programmers often struggle with 'quoting hell'
@ -144,17 +163,21 @@ quoting](https://unix.stackexchange.com/questions/171346/security-implications-o
f-forgetting-to-quote-a-variable-in-bash-posix-shells) f-forgetting-to-quote-a-variable-in-bash-posix-shells)
Whenever you are processing input from from untrusted sources (messages, files, Whenever you are processing input from from untrusted sources (messages, files,
network) you must be as carefull as possible, e.g. set IFS appropriate, disable network) you must be as careful as possible, e.g. set IFS appropriate, disable
globbing (set -f) and quote everthing. In addition delete unused scripts and globbing (set -f) and quote everything. In addition delete unused scripts and
examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable
all not used commands. all not used commands.
**Note:** Until v0.941 (mai/22/2020) telegram-bot-bash has a remote code **Note:** Until v0.941 (mai/22/2020) telegram-bot-bash had a remote code
execution bug, pls update if you use an older version! execution (RCE) bug, pls update if you use an older version!
see [Issue #125](https://github.com/topkecleon/telegram-bot-bash/issues/125)
One of the most powerful features of unix shells like bash is variable and One of the most powerful features of unix shells like bash is variable and
command substitution, this can lead to RCE and information disclosing bugs if command substitution using ```${}``` and ```$()```,
you do not escape '$' porperly, see [Issue but as they are expanded in double quotes, this can lead to RCE and information
#125](https://github.com/topkecleon/telegram-bot-bash/issues/125) disclosing bugs in complex scripts like bashbot
even bash does much to avoid this. So it's more secure to escape or remove '$'
in input from user, files or network.
A powerful tool to improve your scripts is ```shellcheck```. You can [use it A powerful tool to improve your scripts is ```shellcheck```. You can [use it
online](https://www.shellcheck.net/) or [install shellcheck online](https://www.shellcheck.net/) or [install shellcheck
@ -203,8 +226,8 @@ If you are a BSD / MacOS user or must to use an other bash location, see
**I recommend to run your bot as a user, with almost no access rights.** **I recommend to run your bot as a user, with almost no access rights.**
All files your Bot have write access to are in danger to be overwritten/deleted All files your Bot have write access to are in danger to be overwritten/deleted
if your bot is hacked. if your bot is hacked.
For the same reason ervery file your Bot can read is in danger to be disclosed. For the same reason every file your Bot can read is in danger to be disclosed.
Restict your Bots access rigths to the absolute minimum. Restict your Bots access rights to the absolute minimum.
**Never run your Bot as root, this is the most dangerous you can do!** Usually **Never run your Bot as root, this is the most dangerous you can do!** Usually
the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert
@ -217,7 +240,7 @@ Bot is in!
Everyone with read access to your Bot files can extract your Bots data. Everyone with read access to your Bot files can extract your Bots data.
Especially your Bot Token in ```token``` must be protected against other users. Especially your Bot Token in ```token``` must be protected against other users.
No one exept you must have write access to the Bot files. The Bot must be No one except you must have write access to the Bot files. The Bot must be
restricted to have write access to ```count``` and ```tmp-bot-bash``` only, restricted to have write access to ```count``` and ```tmp-bot-bash``` only,
all other files must be write protected. all other files must be write protected.
@ -236,10 +259,10 @@ for the bot commands you wrote and you should know about the risks ...
execution bug, pls update if you use an older version! execution bug, pls update if you use an older version!
### Why Bash and not the much better xyz? ### Why Bash and not the much better xyz?
Well, thats a damn good question ... may be because I'm an Unix/Linux admin Well, that's a damn good question ... may be because I'm an Unix/Linux admin
from stone age. Nevertheless there are more reasons from my side: from stone age. Nevertheless there are more reasons from my side:
- bashbot will run everywhere where bash is availible, from ebedded linux to - bashbot will run everywhere where bash is available, from embedded linux to
mainframe mainframe
- easy to integrate with other shell script, e.g. for sending system message / - easy to integrate with other shell script, e.g. for sending system message /
health status health status
@ -260,7 +283,7 @@ down Version of your Bot](doc/7_develop.md)
### Can I send messages from CLI and scripts? ### Can I send messages from CLI and scripts?
Of course, you can send messages from CLI and scripts, simply install bashbot Of course, you can send messages from CLI and scripts, simply install bashbot
as [described here](#Your-really-first-bashbot-in-a-nutshell), as [described here](#Your-really-first-bashbot-in-a-nutshell),
send the messsage '/start' to set yourself as botadmin and stop the bot with send the message '/start' to set yourself as botadmin and stop the bot with
```./bashbot.sh kill```. ```./bashbot.sh kill```.
Run the following commands in your bash shell or script while you are in the Run the following commands in your bash shell or script while you are in the
@ -280,9 +303,14 @@ send_message "$(<"$BOTADMIN")" "$(df -h)"
For more information see [Expert Use](doc/8_custom.md) For more information see [Expert Use](doc/8_custom.md)
### Why do I get "EXPECTED value GOT EOF" on start? ### Blocked by telegram?
May be your IP is blocked by telegram. You can test this by running curl or This may happen if to many wrong requests are sent to api.telegram.org, e.g.
wget manually: using a wrong token or not existing API calls. If you have a fixed IP you can
ask telegram service to unblock your ip or change your IP. If you are running a
tor proxy on your server you may uncomment the ```BASHBOT_CURL_ARGS``` line in
'mycommands.sh'
You can test if younare blockeds by running curl or wget manually:
```bash ```bash
curl -m 10 https://api.telegram.org/bot curl -m 10 https://api.telegram.org/bot
#curl: (28) Connection timed out after 10001 milliseconds #curl: (28) Connection timed out after 10001 milliseconds
@ -291,12 +319,6 @@ wget -t 1 -T 10 https://api.telegram.org/bot
#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... #Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443...
failed: Connection timed out. failed: Connection timed out.
``` ```
This may happen if to many wrong requests are sent to api.telegram.org, e.g.
using a wrong token or not existing API calls. If you have a fixed IP you can
ask telegram service to unblock your ip or change your IP. If you are running a
socks or tor proxy on your server look for the ```BASHBOT_CURL_ARGS``` lines
in 'mycommands.sh' as example.
@Gnadelwartz @Gnadelwartz
@ -305,4 +327,4 @@ in 'mycommands.sh' as example.
If you feel that there's something missing or if you found a bug, feel free to If you feel that there's something missing or if you found a bug, feel free to
submit a pull request! submit a pull request!
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -4,7 +4,7 @@
# this addon counts how many files, e.g. stickers, are sent to # this addon counts how many files, e.g. stickers, are sent to
# a chat and takes actions if threshold is reached # a chat and takes actions if threshold is reached
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# used events: # used events:
# #
@ -18,7 +18,7 @@
# $1 event: init, startbot ... # $1 event: init, startbot ...
# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic # $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic
# #
# prameters on events # parameters on events
# $1 event: inline, message, ..., file # $1 event: inline, message, ..., file
# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic # $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic
# #

View File

@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
# file: addons/example.sh.dist # file: addons/example.sh.dist
# #
# Addons can register to bashbot events at statup # Addons can register to bashbot events at startup
# by providing their name and a callback per event # by providing their name and a callback per event
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# #
# If an event occours each registered event function is called. # If an event occurs each registered event function is called.
# #
# Events run in the same context as the main bashbot event loop # Events run in the same context as the main bashbot event loop
# so variables set here are persistent as long bashbot is running. # so variables set here are persistent as long bashbot is running.
@ -16,7 +16,7 @@
# e.g. "(long running) &" # e.g. "(long running) &"
# #
# Availible events: # Available events:
# on events startbot and init, this file is sourced # on events startbot and init, this file is sourced
# #
# BASHBOT_EVENT_INLINE inline query received # BASHBOT_EVENT_INLINE inline query received
@ -30,7 +30,7 @@
# BASHBOT_EVENT_FILE file received # BASHBOT_EVENT_FILE file received
# #
# BAHSBOT_EVENT_TIMER this event is a bit special as it fires every Minute # BAHSBOT_EVENT_TIMER this event is a bit special as it fires every Minute
# and has 3 meanings: oneshot, everytime, every X minutes. # and has 3 meanings: oneshot, every time, every X minutes.
# #
# all global variables and functions can be used in registered functions. # all global variables and functions can be used in registered functions.
# #
@ -38,7 +38,7 @@
# $1 event: init, startbot ... # $1 event: init, startbot ...
# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic # $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic
# #
# prameters on events # parameters on events
# $1 event: inline, message, ..., file # $1 event: inline, message, ..., file
# $2 key: key of array BASHBOT_EVENT_xxx # $2 key: key of array BASHBOT_EVENT_xxx
# $3 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic # $3 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# description: Start or stop telegram-bash-bot # description: Start or stop telegram-bash-bot
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# shellcheck disable=SC2009 # shellcheck disable=SC2009
# shellcheck disable=SC2181 # shellcheck disable=SC2181
@ -18,7 +18,7 @@
# save default values # save default values
TERM="" # disable bashbot clear and color output TERM="" # disable bashbot clear and color output
runas="nobody" runas="nobody"
runcmd="echo Dry run:" # not actived until you edit lines below runcmd="echo Dry run:" # not activated until you edit lines below
####################### #######################
# Configuration Section # Configuration Section
@ -45,7 +45,7 @@ case "$1" in
RETVAL=$? RETVAL=$?
;; ;;
'stop') 'stop')
$runcmd "$start kill" $runcmd "$start stop"
RETVAL=$? RETVAL=$?
;; ;;
'status') 'status')

View File

@ -11,19 +11,20 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-73-ge7739b8
# #
# Exit Codes: # Exit Codes:
# - 0 sucess (hopefully) # - 0 success (hopefully)
# - 1 can't change to dir # - 1 can't change to dir
# - 2 can't write to tmp, count or token # - 2 can't write to tmp, count or token
# - 3 user / command / file not found # - 3 user / command / file not found
# - 4 unkown command # - 4 unknown command
# - 5 cannot connect to telegram bot # - 5 cannot connect to telegram bot
# - 6 mandatory module not found # - 6 mandatory module not found
# - 6 can't get bottoken
# shellcheck disable=SC2140,SC2031,SC2120,SC1091 # shellcheck disable=SC2140,SC2031,SC2120,SC1091
# are we runnig in a terminal? # are we running in a terminal?
if [ -t 1 ] && [ -n "$TERM" ]; then if [ -t 1 ] && [ -n "$TERM" ]; then
CLEAR='clear' CLEAR='clear'
RED='\e[31m' RED='\e[31m'
@ -41,7 +42,8 @@ _exists() {
# execute function if exists # execute function if exists
_exec_if_function() { _exec_if_function() {
[ "$(LC_ALL=C type -t "${1}")" != "function" ] || "$@" [ "$(LC_ALL=C type -t "${1}")" != "function" ] && return 1
"$@"
} }
# returns true if function exist # returns true if function exist
_is_function() { _is_function() {
@ -53,21 +55,14 @@ _round_float() {
local digit="${2}"; [[ "${2}" =~ ^[0-9]+$ ]] || digit="0" local digit="${2}"; [[ "${2}" =~ ^[0-9]+$ ]] || digit="0"
LC_ALL=C printf "%.${digit}f" "${1}" LC_ALL=C printf "%.${digit}f" "${1}"
} }
# read JSON.sh style data and asssign to an ARRAY setConfigKey() {
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
Json2Array() { [ -z "${BOTCONFIG}" ] && return 1
# shellcheck source=./commands.sh printf '["%s"]\t"%s"\n' "${1//,/\",\"}" "${2//\"/\\\"}" >>"${BOTCONFIG}.jssh"
[ -z "$1" ] || source <( printf "$1"'=( %s )' "$(sed -E -n -e '/\["[-0-9a-zA-Z_,."]+"\]\+*\t/ s/\t/=/gp' -e 's/=(true|false)/="\1"/')" )
} }
# output ARRAY as JSON.sh style data getConfigKey() {
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
Array2Json() { [ -r "${BOTCONFIG}.jssh" ] && sed -n 's/\["'"$1"'"\]\t*"\(.*\)"/\1/p' <"${BOTCONFIG}.jssh" | tail -n 1
local key
declare -n ARRAY="$1"
for key in "${!ARRAY[@]}"
do
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${ARRAY[${key}]//\"/\\\"}"
done
} }
# get location and name of bashbot.sh # get location and name of bashbot.sh
@ -83,6 +78,19 @@ if [ "${SCRIPT}" != "${REALME}" ] || [ "$1" = "source" ]; then
SOURCE="yes" SOURCE="yes"
fi fi
BOTCOMMANDS="start, stop, status, help, init, stats, broadcast, suspendback, resumeback, killback"
[[ -z "$1" && -z "${SOURCE}" ]] && echo -e "${ORANGE}Available commands: ${GREY}${BOTCOMMANDS}${NC}" && exit
if [ "$1" = "help" ]; then
HELP="README"
if [ -n "${CLEAR}" ];then
_exists w3m && w3m "$HELP.html" && exit
_exists lynx && lynx "$HELP.html" && exit
_exists less && less "$HELP.txt" && exit
fi
cat "$HELP.txt"
exit
fi
if [ -n "$BASHBOT_HOME" ]; then if [ -n "$BASHBOT_HOME" ]; then
SCRIPTDIR="$BASHBOT_HOME" SCRIPTDIR="$BASHBOT_HOME"
else else
@ -94,8 +102,8 @@ fi
ADDONDIR="${BASHBOT_ETC:-.}/addons" ADDONDIR="${BASHBOT_ETC:-.}/addons"
RUNUSER="${USER}" # USER is overwritten by bashbot array RUNUSER="${USER}" # USER is overwritten by bashbot array
# OK everthing setup, lest start # OK everything setup, lest start
if [ "${SOURCE}" != "yes" ] && [ -z "$BASHBOT_HOME" ] && ! cd "${RUNDIR}" ; then if [[ -z "${SOURCE}" && -z "$BASHBOT_HOME" ]] && ! cd "${RUNDIR}" ; then
echo -e "${RED}ERROR: Can't change to ${RUNDIR} ...${NC}" echo -e "${RED}ERROR: Can't change to ${RUNDIR} ...${NC}"
exit 1 exit 1
else else
@ -108,6 +116,7 @@ if [ ! -w "." ]; then
fi fi
# Setup and check environment if BOTTOKEN is NOT set # Setup and check environment if BOTTOKEN is NOT set
BOTCONFIG="${BASHBOT_ETC:-.}/botconfig"
TOKENFILE="${BASHBOT_ETC:-.}/token" TOKENFILE="${BASHBOT_ETC:-.}/token"
BOTADMIN="${BASHBOT_ETC:-.}/botadmin" BOTADMIN="${BASHBOT_ETC:-.}/botadmin"
BOTACL="${BASHBOT_ETC:-.}/botacl" BOTACL="${BASHBOT_ETC:-.}/botacl"
@ -119,41 +128,49 @@ LOGDIR="${RUNDIR:-.}/logs"
if [ ! -d "${LOGDIR}" ] || [ ! -w "${LOGDIR}" ]; then if [ ! -d "${LOGDIR}" ] || [ ! -w "${LOGDIR}" ]; then
LOGDIR="${RUNDIR:-.}" LOGDIR="${RUNDIR:-.}"
fi fi
DEBUGLOG="${LOGDIR}/DEBUG.log"
ERRORLOG="${LOGDIR}/ERROR.log" ERRORLOG="${LOGDIR}/ERROR.log"
UPDATELOG="${LOGDIR}/BASHBOT.log"
# we assume everthing is already set up correctly if we have TOKEN # we assume everything is already set up correctly if we have TOKEN
if [ -z "${BOTTOKEN}" ]; then if [ -z "${BOTTOKEN}" ]; then
# BOTTOKEN empty read from file # BOTCONFIG does not exist, create
if [ ! -f "${TOKENFILE}" ]; then [ ! -f "${BOTCONFIG}.jssh" ] &&
if [ -z "${CLEAR}" ] && [ "$1" != "init" ]; then printf '["bot_config_key"]\t"config_key_value"\n' >>"${BOTCONFIG}.jssh"
# BOTTOKEN empty read ask user
if [ -z "$(getConfigKey "bottoken")" ]; then
# convert old token
if [ -r "${TOKENFILE}" ]; then
token="$(< "${TOKENFILE}")"
# no old token available ask user
elif [ -z "${CLEAR}" ] && [ "$1" != "init" ]; then
echo "Running headless, set BOTTOKEN or run ${SCRIPT} init first!" echo "Running headless, set BOTTOKEN or run ${SCRIPT} init first!"
exit 2 exit 2
else else
${CLEAR} ${CLEAR}
echo -e "${RED}TOKEN MISSING.${NC}" echo -e "${RED}TOKEN MISSING.${NC}"
echo -e "${ORANGE}PLEASE WRITE YOUR TOKEN HERE OR PRESS CTRL+C TO ABORT${NC}" echo -e "${ORANGE}PLEASE WRITE YOUR TOKEN HERE OR PRESS CTRL+C TO ABORT${NC}"
read -r BOTTOKEN read -r token
printf '%s\n' "${BOTTOKEN}" > "${TOKENFILE}"
fi fi
[ -n "${token}" ] && printf '["bottoken"]\t"%s"\n' "${token}" >> "${BOTCONFIG}.jssh"
fi fi
# read BOTTOKEN from file and removen everyting from first newline to end
BOTTOKEN="$(< "${TOKENFILE}")"
BOTTOKEN="${BOTTOKEN%%$'\n'*}"
# setup botadmin file # setup botadmin file
if [ ! -f "${BOTADMIN}" ]; then if [ -z "$(getConfigKey "botadmin")" ]; then
if [ -z "${CLEAR}" ]; then # convert old admin
if [ -r "${BOTADMIN}" ]; then
admin="$(< "${BOTADMIN}")"
elif [ -z "${CLEAR}" ]; then
echo "Running headless, set botadmin to AUTO MODE!" echo "Running headless, set botadmin to AUTO MODE!"
printf '%s\n' '?' > "${BOTADMIN}"
else else
${CLEAR} ${CLEAR}
echo -e "${RED}BOTADMIN MISSING.${NC}" echo -e "${RED}BOTADMIN MISSING.${NC}"
echo -e "${ORANGE}PLEASE WRITE YOUR TELEGRAM ID HERE OR ENTER '?'${NC}" echo -e "${ORANGE}PLEASE WRITE YOUR TELEGRAM ID HERE OR ENTER '?'${NC}"
echo -e "${ORANGE}TO MAKE FIRST USER TYPING '/start' TO BOTADMIN${NC}" echo -e "${ORANGE}TO MAKE FIRST USER TYPING '/start' TO BOTADMIN${NC}"
read -r admin read -r admin
[ -z "${admin}" ] && admin='?'
printf '%s\n' "${admin}" > "${BOTADMIN}"
fi fi
[ -z "${admin}" ] && admin='?'
printf '["botadmin"]\t"%s"\n' "${admin}" >> "${BOTCONFIG}.jssh"
fi fi
# setup botacl file # setup botacl file
if [ ! -f "${BOTACL}" ]; then if [ ! -f "${BOTACL}" ]; then
@ -170,7 +187,7 @@ if [ -z "${BOTTOKEN}" ]; then
fi fi
# setup count file # setup count file
if [ ! -f "${COUNTFILE}.jssh" ]; then if [ ! -f "${COUNTFILE}.jssh" ]; then
printf '["counted_user_chat_id"]\t"num_messages_seen"\n' > "${COUNTFILE}.jssh" printf '["counted_user_chat_id"]\t"num_messages_seen"\n' >> "${COUNTFILE}.jssh"
# convert old file on creation # convert old file on creation
if [ -r "${COUNTFILE}" ];then if [ -r "${COUNTFILE}" ];then
sed 's/COUNT/\[\"/;s/$/\"\]\t\"1\"/' < "${COUNTFILE}" >> "${COUNTFILE}.jssh" sed 's/COUNT/\[\"/;s/$/\"\]\t\"1\"/' < "${COUNTFILE}" >> "${COUNTFILE}.jssh"
@ -182,17 +199,29 @@ if [ -z "${BOTTOKEN}" ]; then
fi fi
# setup blocked file # setup blocked file
if [ ! -f "${BLOCKEDFILE}.jssh" ]; then if [ ! -f "${BLOCKEDFILE}.jssh" ]; then
printf '["blocked_user_or_chat_id"]\t"name and reason"\n' >"${BLOCKEDFILE}.jssh" printf '["blocked_user_or_chat_id"]\t"name and reason"\n' >>"${BLOCKEDFILE}.jssh"
fi fi
fi fi
# do we have BSD sed # read BOTTOKEN from bot database if not set
if ! sed '1ia' </dev/null 2>/dev/null; then if [ -z "${BOTTOKEN}" ]; then
echo -e "${ORANGE}Warning: You may run on a BSD style system without gnu utils ...${NC}" BOTTOKEN="$(getConfigKey "bottoken")"
if [ -z "${BOTTOKEN}" ]; then
echo -e "${ORANGE}Warning: can't get bot token, try to recover working config.${NC}"
if [ -r "${BOTCONFIG}.jssh.ok" ]; then
cp "${BOTCONFIG}.jssh.ok" "${BOTCONFIG}.jssh"
BOTTOKEN="$(getConfigKey "bottoken")"
else
echo -e "${RED}Error: Missing bot token! remove ${BOTCONFIG}.jssh and run \"bashbot.sh init\" may fix it.${NC}"
exit 7
fi
fi
fi fi
# BOTTOKEN format checks # BOTTOKEN format checks
if [[ ! "${BOTTOKEN}" =~ ^[0-9]{8,10}:[a-zA-Z0-9_-]{35}$ ]]; then if [[ ! "${BOTTOKEN}" =~ ^[0-9]{8,10}:[a-zA-Z0-9_-]{35}$ ]]; then
echo -e "${ORANGE}Warning, your bottoken may incorrect. it should have the following format:${NC}" echo -e "${ORANGE}Warning: your bottoken may incorrect. it should have the following format:${NC}"
echo -e "${GREY}123456789${RED}:${GREY}Aa-Zz_0Aa-Zz_1Aa-Zz_2Aa-Zz_3Aa-Zz_4${ORANGE} => ${NC}\c" echo -e "${GREY}123456789${RED}:${GREY}Aa-Zz_0Aa-Zz_1Aa-Zz_2Aa-Zz_3Aa-Zz_4${ORANGE} => ${NC}\c"
echo -e "${GREY}8-10 digits${RED}:${GREY}35 alnum characters + '_-'${NC}" echo -e "${GREY}8-10 digits${RED}:${GREY}35 alnum characters + '_-'${NC}"
echo -e "${ORANGE}Your current token is: '${GREY}^$(cat -ve <<<"${BOTTOKEN//:/${RED}:${GREY}}")${ORANGE}'${NC}" echo -e "${ORANGE}Your current token is: '${GREY}^$(cat -ve <<<"${BOTTOKEN//:/${RED}:${GREY}}")${ORANGE}'${NC}"
@ -213,6 +242,9 @@ ME_URL=$URL'/getMe'
UPD_URL=$URL'/getUpdates?offset=' UPD_URL=$URL'/getUpdates?offset='
GETFILE_URL=$URL'/getFile' GETFILE_URL=$URL'/getFile'
#################
# BASHBOT COMMON functions
declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR TOKENFILE BOTADMIN BOTACL DATADIR COUNTFILE declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR TOKENFILE BOTADMIN BOTACL DATADIR COUNTFILE
declare -rx BOTTOKEN URL ME_URL UPD_URL GETFILE_URL declare -rx BOTTOKEN URL ME_URL UPD_URL GETFILE_URL
@ -224,7 +256,7 @@ export res CAPTION
################## ##################
# read commamds file if we are not sourced # read commamds file if we are not sourced
COMMANDS="${BASHBOT_ETC:-.}/commands.sh" COMMANDS="${BASHBOT_ETC:-.}/commands.sh"
if [ "${SOURCE}" != "yes" ]; then if [ -z "${SOURCE}" ]; then
if [ ! -f "${COMMANDS}" ] || [ ! -r "${COMMANDS}" ]; then if [ ! -f "${COMMANDS}" ] || [ ! -r "${COMMANDS}" ]; then
echo -e "${RED}ERROR: ${COMMANDS} does not exist or is not readable!.${NC}" echo -e "${RED}ERROR: ${COMMANDS} does not exist or is not readable!.${NC}"
ls -l "${COMMANDS}" ls -l "${COMMANDS}"
@ -245,14 +277,17 @@ done
# BASHBOT INTERNAL functions # BASHBOT INTERNAL functions
# #
# do we have BSD sed
if ! sed '1ia' </dev/null 2>/dev/null; then
echo -e "${ORANGE}Warning: You may run on a BSD style system without gnu utils ...${NC}"
fi
#jsonDB is now mandatory #jsonDB is now mandatory
if ! _is_function jssh_newDB ; then if ! _is_function jssh_newDB ; then
echo -e "${RED}ERROR: Mandatory module jsonDB is missing or not readable!" echo -e "${RED}ERROR: Mandatory module jsonDB is missing or not readable!"
exit 6 exit 6
fi fi
#################
# BASHBOT COMMON functions
# $1 URL, $2 filename in DATADIR # $1 URL, $2 filename in DATADIR
# outputs final filename # outputs final filename
download() { download() {
@ -269,14 +304,14 @@ procname(){
printf '%s\n' "$2${ME}_$1" printf '%s\n' "$2${ME}_$1"
} }
# $1 sting to search for proramm incl. parameters # $1 string to search for proramm incl. parameters
# retruns a list of PIDs of all current bot proceeses matching $1 # returns a list of PIDs of all current bot proceeses matching $1
proclist() { proclist() {
# shellcheck disable=SC2009 # shellcheck disable=SC2009
ps -fu "${UID}" | grep -F "$1" | grep -v ' grep'| grep -F "${ME}" | sed 's/\s\+/\t/g' | cut -f 2 ps -fu "${UID}" | grep -F "$1" | grep -v ' grep'| grep -F "${ME}" | sed 's/\s\+/\t/g' | cut -f 2
} }
# $1 sting to search for proramm to kill # $1 string to search for proramm to kill
killallproc() { killallproc() {
local procid; procid="$(proclist "$1")" local procid; procid="$(proclist "$1")"
if [ -n "${procid}" ] ; then if [ -n "${procid}" ] ; then
@ -290,8 +325,10 @@ killallproc() {
} }
# $ chat $2 mesgid $3 nolog
declare -xr DELETE_URL=$URL'/deleteMessage' declare -xr DELETE_URL=$URL'/deleteMessage'
delete_message() { delete_message() {
[ -z "$3" ] && printf "%s: Delete Message CHAT=%s MSG_ID=%s\n" "$(date)" "${1}" "${2}" >>"${UPDATELOG}"
sendJson "${1}" '"message_id": '"${2}"'' "${DELETE_URL}" sendJson "${1}" '"message_id": '"${2}"'' "${DELETE_URL}"
} }
@ -301,7 +338,7 @@ get_file() {
printf '%s\n' "${URL}"/"$(JsonGetString <<< "${res}" '"result","file_path"')" printf '%s\n' "${URL}"/"$(JsonGetString <<< "${res}" '"result","file_path"')"
} }
# curl is preffered, but may not availible on ebedded systems # curl is preferred, but may not available on embedded systems
TIMEOUT="${BASHBOT_TIMEOUT}" TIMEOUT="${BASHBOT_TIMEOUT}"
[[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT="20" [[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT="20"
@ -309,6 +346,7 @@ if [ -z "${BASHBOT_WGET}" ] && _exists curl ; then
[ -z "${BASHBOT_CURL}" ] && BASHBOT_CURL="curl" [ -z "${BASHBOT_CURL}" ] && BASHBOT_CURL="curl"
# simple curl or wget call, output to stdout # simple curl or wget call, output to stdout
getJson(){ getJson(){
[[ -n "${BASHBOTDEBUG}" && -n "${3}" ]] && printf "%s: getJson (curl) URL=%s\n" "$(date)" "${1##*/}" 1>&2
# shellcheck disable=SC2086 # shellcheck disable=SC2086
"${BASHBOT_CURL}" -sL -k ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" "$1" "${BASHBOT_CURL}" -sL -k ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" "$1"
} }
@ -316,6 +354,7 @@ if [ -z "${BASHBOT_WGET}" ] && _exists curl ; then
sendJson(){ sendJson(){
local chat=""; local chat="";
[ -n "${1}" ] && chat='"chat_id":'"${1}"',' [ -n "${1}" ] && chat='"chat_id":'"${1}"','
[ -n "${BASHBOTDEBUG}" ] && printf "%s: sendJson (curl) CHAT=%s JSON=%s URL=%s\n" "$(date)" "${1}" "${2:0:100}" "${3##*/}" 1>&2
# shellcheck disable=SC2086 # shellcheck disable=SC2086
res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}"\ res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}"\
-d '{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' -X POST "${3}" \ -d '{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' -X POST "${3}" \
@ -326,19 +365,21 @@ if [ -z "${BASHBOT_WGET}" ] && _exists curl ; then
sendUpload() { sendUpload() {
[ "$#" -lt 4 ] && return [ "$#" -lt 4 ] && return
if [ -n "$5" ]; then if [ -n "$5" ]; then
[ -n "${BASHBOTDEBUG}" ] && printf "%s: sendUpload CHAT=%s WHAT=%s FILE=%s CAPT=%s\n" "$(date)" "${1}" "${2}" "${3}" "${4}" 1>&2
# shellcheck disable=SC2086 # shellcheck disable=SC2086
res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1"\ res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1"\
-F "$2=@$3;${3##*/}" -F "caption=$5" | "${JSONSHFILE}" -s -b -n )" -F "$2=@$3;${3##*/}" -F "caption=$5" | "${JSONSHFILE}" -s -b -n 2>/dev/null )"
else else
# shellcheck disable=SC2086 # shellcheck disable=SC2086
res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1"\ res="$("${BASHBOT_CURL}" -s -k ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1"\
-F "$2=@$3;${3##*/}" | "${JSONSHFILE}" -s -b -n )" -F "$2=@$3;${3##*/}" | "${JSONSHFILE}" -s -b -n 2>/dev/null )"
fi fi
sendJsonResult "${res}" "sendUpload (curl)" "$@" sendJsonResult "${res}" "sendUpload (curl)" "$@"
} }
else else
# simple curl or wget call outputs result to stdout # simple curl or wget call outputs result to stdout
getJson(){ getJson(){
[[ -n "${BASHBOTDEBUG}" && -z "${3}" ]] && printf "%s: getJson (wget) URL=%s\n" "$(date)" "${1##*/}" 1>&2
# shellcheck disable=SC2086 # shellcheck disable=SC2086
wget --no-check-certificate -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - "$1" wget --no-check-certificate -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - "$1"
} }
@ -346,6 +387,7 @@ else
sendJson(){ sendJson(){
local chat=""; local chat="";
[ -n "${1}" ] && chat='"chat_id":'"${1}"',' [ -n "${1}" ] && chat='"chat_id":'"${1}"','
[ -n "${BASHBOTDEBUG}" ] && printf "%s: sendJson (wget) CHAT=%s JSON=%s URL=%s\n" "$(date)" "${1}" "${2:0:100}" "${3##*/}" 1>&2
# shellcheck disable=SC2086 # shellcheck disable=SC2086
res="$(wget --no-check-certificate -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - --post-data='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' \ res="$(wget --no-check-certificate -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - --post-data='{'"${chat} $(iconv -f utf-8 -t utf-8 -c <<<$2)"'}' \
--header='Content-Type:application/json' "${3}" | "${JSONSHFILE}" -s -b -n 2>/dev/null )" --header='Content-Type:application/json' "${3}" | "${JSONSHFILE}" -s -b -n 2>/dev/null )"
@ -354,7 +396,7 @@ else
sendUpload() { sendUpload() {
printf "%s: %s\n" "$(date)" "Sorry, wget does not support file upload\n" >>"${ERRORLOG}" printf "%s: %s\n" "$(date)" "Sorry, wget does not support file upload\n" >>"${ERRORLOG}"
BOTSENT[OK]="false" BOTSENT[OK]="false"
[ "${SOURCE}" != "yes" ] && [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "upload" "$@" & [[ -z "${SOURCE}" && -n "${BASHBOT_EVENT_SEND[*]}" ]] && event_send "upload" "$@" &
} }
fi fi
@ -376,20 +418,22 @@ sendJsonRetry(){
return return
;; ;;
esac esac
[ "${BOTSENT[OK]}" = "true" ] && printf "%s: Retry OK: %s %s %s\n" "$(date)" "${retry}" "${1}" "${2:0:60}" [ "${BOTSENT[OK]}" = "true" ] && printf "%s: Retry OK: %s %s %s\n" "$(date)" "${retry}" "${1}" "${2:0:60}"
} >>"${ERRORLOG}" } >>"${ERRORLOG}"
# process sendJson result # process sendJson result
# stdout is written to ERROR.log # stdout is written to ERROR.log
# $1 result $2 function $3 .. $n original arguments, $3 is Chat_id # $1 result $2 function $3 .. $n original arguments, $3 is Chat_id
sendJsonResult(){ sendJsonResult(){
local offset=0
BOTSENT=( ) BOTSENT=( )
[ -n "${BASHBOTDEBUG}" ] && printf "\n%s: New Result ==========\n%s\n" "$(date)" "$1" >>"${LOGDIR}/MESSAGE.log"
BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "${1}")" BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "${1}")"
if [ "${BOTSENT[OK]}" = "true" ]; then if [ "${BOTSENT[OK]}" = "true" ]; then
BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "${1}")" BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "${1}")"
[ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "send" "${@:3}" [ -n "${BASHBOT_EVENT_SEND[*]}" ] && event_send "send" "${@:3}"
return return
# hot path everthing OK! # hot path everything OK!
else else
# oops something went wrong! # oops something went wrong!
if [ "${res}" != "" ]; then if [ "${res}" != "" ]; then
@ -397,12 +441,15 @@ sendJsonResult(){
BOTSENT[DESCRIPTION]="$(JsonGetString '"description"' <<< "${1}")" BOTSENT[DESCRIPTION]="$(JsonGetString '"description"' <<< "${1}")"
BOTSENT[RETRY]="$(JsonGetValue '"parameters","retry_after"' <<< "${1}")" BOTSENT[RETRY]="$(JsonGetValue '"parameters","retry_after"' <<< "${1}")"
else else
BOTSENT[OK]="false"
BOTSENT[ERROR]="999" BOTSENT[ERROR]="999"
BOTSENT[DESCRIPTION]="Send to telegram not possible, timeout/broken/no connection" BOTSENT[DESCRIPTION]="Send to telegram not possible, timeout/broken/no connection"
fi fi
# log error # log error
[[ "${BOTSENT[ERROR]}" = "400" && "${BOTSENT[DESCRIPTION]}" == *"starting at byte offset"* ]] &&\
offset="${BOTSENT[DESCRIPTION]%* }"
printf "%s: RESULT=%s FUNC=%s CHAT[ID]=%s ERROR=%s DESC=%s ACTION=%s\n" "$(date)"\ printf "%s: RESULT=%s FUNC=%s CHAT[ID]=%s ERROR=%s DESC=%s ACTION=%s\n" "$(date)"\
"${BOTSENT[OK]}" "${2}" "${3}" "${BOTSENT[ERROR]}" "${BOTSENT[DESCRIPTION]}" "${4:0:60}" "${BOTSENT[OK]}" "${2}" "${3}" "${BOTSENT[ERROR]}" "${BOTSENT[DESCRIPTION]}" "${4:${offset}:100}"
# warm path, do not retry on error, also if we use wegt # warm path, do not retry on error, also if we use wegt
[ -n "${BASHBOT_RETRY}${BASHBOT_WGET}" ] && return [ -n "${BASHBOT_RETRY}${BASHBOT_WGET}" ] && return
@ -419,7 +466,7 @@ sendJsonResult(){
if [ "${BOTSENT[ERROR]}" == "999" ];then if [ "${BOTSENT[ERROR]}" == "999" ];then
# check if default curl and args are OK # check if default curl and args are OK
if ! curl -sL -k -m 2 "${URL}" >/dev/null 2>&1 ; then if ! curl -sL -k -m 2 "${URL}" >/dev/null 2>&1 ; then
printf "%s: BASHBOT IP Adress is blocked!\n" "$(date)" printf "%s: BASHBOT IP Address is blocked!\n" "$(date)"
# user provided function to recover or notify block # user provided function to recover or notify block
if _exec_if_function bashbotBlockRecover; then if _exec_if_function bashbotBlockRecover; then
BASHBOT_RETRY="2" BASHBOT_RETRY="2"
@ -441,7 +488,7 @@ sendJsonResult(){
fi fi
} >>"${ERRORLOG}" } >>"${ERRORLOG}"
# escape / remove text charaters for json strings, eg. " -> \" # escape / remove text characters for json strings, eg. " -> \"
# $1 string # $1 string
# output escaped string # output escaped string
JsonEscape(){ JsonEscape(){
@ -455,7 +502,7 @@ title2Json(){
[ -n "$1" ] && title=',"title":"'$(JsonEscape "$1")'"' [ -n "$1" ] && title=',"title":"'$(JsonEscape "$1")'"'
[ -n "$2" ] && caption=',"caption":"'$(JsonEscape "$2")'"' [ -n "$2" ] && caption=',"caption":"'$(JsonEscape "$2")'"'
[ -n "$3" ] && desc=',"description":"'$(JsonEscape "$3")'"' [ -n "$3" ] && desc=',"description":"'$(JsonEscape "$3")'"'
[ -n "$4" ] && markup=',"parse_mode":"'$(JsonEscape "$4")'"' [ -n "$4" ] && markup=',"parse_mode":"'"$4"'"'
[ -n "$5" ] && keyboard=',"reply_markup":"'$(JsonEscape "$5")'"' [ -n "$5" ] && keyboard=',"reply_markup":"'$(JsonEscape "$5")'"'
printf '%s\n' "${title}${caption}${desc}${markup}${keyboard}" printf '%s\n' "${title}${caption}${desc}${markup}${keyboard}"
} }
@ -465,7 +512,7 @@ getBotName() {
getJson "$ME_URL" | "${JSONSHFILE}" -s -b -n 2>/dev/null | JsonGetString '"result","username"' getJson "$ME_URL" | "${JSONSHFILE}" -s -b -n 2>/dev/null | JsonGetString '"result","username"'
} }
# pure bash implementaion, done by KayM (@gnadelwartz) # pure bash implementation, done by KayM (@gnadelwartz)
# see https://stackoverflow.com/a/55666449/9381171 # see https://stackoverflow.com/a/55666449/9381171
JsonDecode() { JsonDecode() {
local out="$1" remain="" U="" local out="$1" remain="" U=""
@ -505,28 +552,35 @@ process_client() {
iQUERY[ID]="${UPD["result",${num},"inline_query","id"]}" iQUERY[ID]="${UPD["result",${num},"inline_query","id"]}"
CHAT[ID]="${UPD["result",${num},"message","chat","id"]}" CHAT[ID]="${UPD["result",${num},"message","chat","id"]}"
USER[ID]="${UPD["result",${num},"message","from","id"]}" USER[ID]="${UPD["result",${num},"message","from","id"]}"
[ -z "${CHAT[ID]}" ] && CHAT[ID]="${UPD["result",${num},"edited_message","chat","id"]}"
[ -z "${USER[ID]}" ] && USER[ID]="${UPD["result",${num},"edited_message","from","id"]}"
# log message on debug # log message on debug
[[ -n "${debug}" ]] && printf "\n%s: New Message ==========\n%s\n" "$(date)" "$UPDATE" >>"${LOGDIR}/MESSAGE.log" [[ -n "${debug}" ]] && printf "\n%s: New Message ==========\n%s\n" "$(date)" "$UPDATE" >>"${LOGDIR}/MESSAGE.log"
# check for uers / groups to ignore # check for uers / groups to ignore
[ -n "${USER[ID]}" ] && [[ " ${!BASHBOT_BLOCKED[*]} " == *" ${USER[ID]} "* ]] && return jssh_updateArray_async "BASHBOTBLOCKED" "${BLOCKEDFILE}"
jssh_readDB_async "BASHBOT_BLOCKED" "${BLOCKEDFILE}" [ -n "${USER[ID]}" ] && [[ -n "${BASHBOTBLOCKED[${USER[ID]}]}" || -n "${BASHBOTBLOCKED[${CHAT[ID]}]}" ]] && return
# process per message type # process per message type
if [ -z "${iQUERY[ID]}" ]; then if [ -z "${iQUERY[ID]}" ]; then
MESSAGE[0]="$(JsonDecode "${UPD["result",${num},"message","text"]}" | sed 's#\\/#/#g')" if grep -qs -e '\["result",'"${num}"',"edited_message"' <<<"${UPDATE}"; then
# edited message
UPDATE="${UPDATE//,${num},\"edited_message\",/,${num},\"message\",}"
Json2Array 'UPD' <<<"${UPDATE}"
MESSAGE[0]="/edited_message "
fi
process_message "${num}" "${debug}" process_message "${num}" "${debug}"
[[ -n "${debug}" ]] && printf "%s: update received FROM=%s CHAT=%s CMD=%s\n"\ printf "%s: update received FROM=%s CHAT=%s CMD=%s\n" "$(date)" "${USER[USERNAME]:0:20} (${USER[ID]})"\
"$(date)" "${USER[USERNAME]:0:20} (${USER[ID]})"\ "${CHAT[USERNAME]:0:20}${CHAT[TITLE]:0:30} (${CHAT[ID]})"\
"${CHAT[USERNAME]:0:20}${CHAT[TITLE]:0:30} (${CHAT[ID]})" "${MESSAGE%%[ \?]*}" "${MESSAGE:0:30}${CAPTION:0:30}${URLS[*]:0:30}" >>"${UPDATELOG}"
else else
process_inline "${num}" "${debug}" process_inline "${num}" "${debug}"
[[ -n "${debug}" ]] && printf "%s: iQuery received FROM=%s iQUERY=%s\n"\ printf "%s: iQuery received FROM=%s iQUERY=%s\n" "$(date)"\
"$(date)" "${iQUERY[USERNAME]} (${iQUERY[USER_ID]})" "${iQUERY[0]}" "${iQUERY[USERNAME]:0:20} (${iQUERY[USER_ID]})" "${iQUERY[0]}" >>"${UPDATELOG}"
fi fi
##### #####
# process inline and message events # process inline and message events
# first classic commnad dispatcher # first classic command dispatcher
# shellcheck source=./commands.sh # shellcheck source=./commands.sh
source "${COMMANDS}" "${debug}" & source "${COMMANDS}" "${debug}" &
@ -594,7 +648,7 @@ event_message() {
# shellcheck disable=SC2153 # shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_MESSAGE[@]}" for key in "${!BASHBOT_EVENT_MESSAGE[@]}"
do do
_exec_if_function "${BASHBOT_EVENT_MESSAGE[${key}]}" "messsage" "${key}" "${debug}" _exec_if_function "${BASHBOT_EVENT_MESSAGE[${key}]}" "message" "${key}" "${debug}"
done done
# ${TEXT[*]} event_text # ${TEXT[*]} event_text
@ -642,7 +696,7 @@ event_message() {
fi fi
# ${VENUE[*]} event_location # ${VENUE[*]} event_location
# ${LOCALTION[*]} event_location # ${LOCATION[*]} event_location
if [ -n "${LOCATION[LONGITUDE]}" ] || [ -n "${VENUE[TITLE]}" ]; then if [ -n "${LOCATION[LONGITUDE]}" ] || [ -n "${VENUE[TITLE]}" ]; then
# shellcheck disable=SC2153 # shellcheck disable=SC2153
for key in "${!BASHBOT_EVENT_LOCATION[@]}" for key in "${!BASHBOT_EVENT_LOCATION[@]}"
@ -673,6 +727,7 @@ process_inline() {
process_message() { process_message() {
local num="$1" local num="$1"
# Message # Message
MESSAGE[0]+="$(JsonDecode "${UPD["result",${num},"message","text"]}" | sed 's#\\/#/#g')"
MESSAGE[ID]="${UPD["result",${num},"message","message_id"]}" MESSAGE[ID]="${UPD["result",${num},"message","message_id"]}"
# Chat ID is now parsed when update isreceived # Chat ID is now parsed when update isreceived
@ -680,21 +735,24 @@ process_message() {
CHAT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","last_name"]}")" CHAT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","last_name"]}")"
CHAT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","first_name"]}")" CHAT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","first_name"]}")"
CHAT[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","chat","username"]}")" CHAT[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","chat","username"]}")"
# set real name as username if empty
[ -z "${CHAT[USERNAME]}" ] && CHAT[USERNAME]="${CHAT[FIRST_NAME]} ${CHAT[LAST_NAME]}"
CHAT[TITLE]="$(JsonDecode "${UPD["result",${num},"message","chat","title"]}")" CHAT[TITLE]="$(JsonDecode "${UPD["result",${num},"message","chat","title"]}")"
CHAT[TYPE]="$(JsonDecode "${UPD["result",${num},"message","chat","type"]}")" CHAT[TYPE]="$(JsonDecode "${UPD["result",${num},"message","chat","type"]}")"
CHAT[ALL_ADMIN]="${UPD["result",${num},"message","chat","all_members_are_administrators"]}" CHAT[ALL_ADMIN]="${UPD["result",${num},"message","chat","all_members_are_administrators"]}"
CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]="${CHAT[ALL_ADMIN]}" # backward compatibility
# user ID is now parsed when update isreceived # user ID is now parsed when update isreceived
#USER[ID]="${UPD["result",${num},"message","from","id"]}" #USER[ID]="${UPD["result",${num},"message","from","id"]}"
USER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","first_name"]}")" USER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","first_name"]}")"
USER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","last_name"]}")" USER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","last_name"]}")"
USER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","from","username"]}")" USER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","from","username"]}")"
# set real name as username if empty
[ -z "${USER[USERNAME]}" ] && USER[USERNAME]="${USER[FIRST_NAME]} ${USER[LAST_NAME]}"
# in reply to message from # in reply to message from
REPLYTO=( ) REPLYTO=( )
REPLYTO[UID]="${UPD["result",${num},"message","reply_to_message","from","id"]}" if grep -qs -e '\["result",'"${num}"',"message","reply_to_message"' <<<"${UPDATE}"; then
if [ -n "${REPLYTO[UID]}" ]; then REPLYTO[UID]="${UPD["result",${num},"message","reply_to_message","from","id"]}"
REPLYTO[0]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","text"]}")" REPLYTO[0]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","text"]}")"
REPLYTO[ID]="${UPD["result",${num},"message","reply_to_message","message_id"]}" REPLYTO[ID]="${UPD["result",${num},"message","reply_to_message","message_id"]}"
REPLYTO[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","first_name"]}")" REPLYTO[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","first_name"]}")"
@ -704,31 +762,28 @@ process_message() {
# forwarded message from # forwarded message from
FORWARD=( ) FORWARD=( )
FORWARD[UID]="${UPD["result",${num},"message","forward_from","id"]}" if grep -qs -e '\["result",'"${num}"',"message","forward_from"' <<<"${UPDATE}"; then
if [ -n "${FORWARD[UID]}" ]; then FORWARD[UID]="${UPD["result",${num},"message","forward_from","id"]}"
FORWARD[ID]="${MESSAGE[ID]}" # same as message ID FORWARD[ID]="${MESSAGE[ID]}" # same as message ID
FORWARD[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","first_name"]}")" FORWARD[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","first_name"]}")"
FORWARD[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","last_name"]}")" FORWARD[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","last_name"]}")"
FORWARD[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","username"]}")" FORWARD[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","username"]}")"
fi fi
# Audio # get file URL from telegram
URLS[AUDIO]="$(get_file "${UPD["result",${num},"message","audio","file_id"]}")" URLS=()
# Document if grep -qs -e '\["result",'"${num}"',"message",".*,"file_id"\]' <<<"${UPDATE}"; then
URLS[DOCUMENT]="$(get_file "${UPD["result",${num},"message","document","file_id"]}")" URLS[AUDIO]="$(get_file "${UPD["result",${num},"message","audio","file_id"]}")"
# Photo URLS[DOCUMENT]="$(get_file "${UPD["result",${num},"message","document","file_id"]}")"
URLS[PHOTO]="$(get_file "${UPD["result",${num},"message","photo",0,"file_id"]}")" URLS[PHOTO]="$(get_file "${UPD["result",${num},"message","photo",0,"file_id"]}")"
# Sticker URLS[STICKER]="$(get_file "${UPD["result",${num},"message","sticker","file_id"]}")"
URLS[STICKER]="$(get_file "${UPD["result",${num},"message","sticker","file_id"]}")" URLS[VIDEO]="$(get_file "${UPD["result",${num},"message","video","file_id"]}")"
# Video URLS[VOICE]="$(get_file "${UPD["result",${num},"message","voice","file_id"]}")"
URLS[VIDEO]="$(get_file "${UPD["result",${num},"message","video","file_id"]}")" fi
# Voice
URLS[VOICE]="$(get_file "${UPD["result",${num},"message","voice","file_id"]}")"
# Contact # Contact
CONTACT=( ) CONTACT=( )
CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","first_name"]}")" if grep -qs -e '\["result",'"${num}"',"message","contact"' <<<"${UPDATE}"; then
if [ -n "${CONTACT[FIRST_NAME]}" ]; then CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","first_name"]}")"
CONTACT[USER_ID]="$(JsonDecode "${UPD["result",${num},"message","contact","user_id"]}")" CONTACT[USER_ID]="$(JsonDecode "${UPD["result",${num},"message","contact","user_id"]}")"
CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","last_name"]}")" CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","last_name"]}")"
CONTACT[NUMBER]="${UPD["result",${num},"message","contact","phone_number"]}" CONTACT[NUMBER]="${UPD["result",${num},"message","contact","phone_number"]}"
@ -737,8 +792,8 @@ process_message() {
# vunue # vunue
VENUE=( ) VENUE=( )
VENUE[TITLE]="$(JsonDecode "${UPD["result",${num},"message","venue","title"]}")" if grep -qs -e '\["result",'"${num}"',"message","venue"' <<<"${UPDATE}"; then
if [ -n "${VENUE[TITLE]}" ]; then VENUE[TITLE]="$(JsonDecode "${UPD["result",${num},"message","venue","title"]}")"
VENUE[ADDRESS]="$(JsonDecode "${UPD["result",${num},"message","venue","address"]}")" VENUE[ADDRESS]="$(JsonDecode "${UPD["result",${num},"message","venue","address"]}")"
VENUE[LONGITUDE]="${UPD["result",${num},"message","venue","location","longitude"]}" VENUE[LONGITUDE]="${UPD["result",${num},"message","venue","location","longitude"]}"
VENUE[LATITUDE]="${UPD["result",${num},"message","venue","location","latitude"]}" VENUE[LATITUDE]="${UPD["result",${num},"message","venue","location","latitude"]}"
@ -753,61 +808,77 @@ process_message() {
LOCATION[LATITUDE]="${UPD["result",${num},"message","location","latitude"]}" LOCATION[LATITUDE]="${UPD["result",${num},"message","location","latitude"]}"
# service messages # service messages
SERVICE=( ); NEWMEMBER=( ) SERVICE=( ); NEWMEMBER=( ); LEFTMEMBER=( )
SERVICE[NEWMEMBER]="${UPD["result",${num},"message","new_chat_member","id"]}" if grep -qs -e '\["result",'"${num}"',"message","new_chat_member' <<<"${UPDATE}"; then
if [ -n "${SERVICE[NEWMEMBER]}" ]; then SERVICE[NEWMEMBER]="${UPD["result",${num},"message","new_chat_member","id"]}"
NEWMEMBER[ID]="${SERVICE[NEWMEMBER]}" NEWMEMBER[ID]="${SERVICE[NEWMEMBER]}"
NEWMEMBER[FIRST_NAME]="${UPD["result",${num},"message","new_chat_member","first_name"]}" NEWMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","first_name"]}")"
NEWMEMBER[LAST_NAME]="${UPD["result",${num},"message","new_chat_member","last_name"]}" NEWMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","last_name"]}")"
NEWMEMBER[USERNAME]="${UPD["result",${num},"message","new_chat_member","username"]}" NEWMEMBER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","new_chat_member","username"]}")"
NEWMEMBER[ISBOT]="${UPD["result",${num},"message","new_chat_member","is_bot"]}" NEWMEMBER[ISBOT]="${UPD["result",${num},"message","new_chat_member","is_bot"]}"
MESSAGE[0]="/new_chat_member ${NEWMEMBER[USERNAME]:=${NEWMEMBER[FIRST_NAME]} ${NEWMEMBER[LAST_NAME]}}"
fi
if grep -qs -e '\["result",'"${num}"',"message","left_chat_member' <<<"${UPDATE}"; then
SERVICE[LEFTMEMBER]="${UPD["result",${num},"message","left_chat_member","id"]}"
LEFTMEMBER[ID]="${SERVICE[LEFTMEBER]}"
LEFTMEMBER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","first_name"]}")"
LEFTMEMBER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","last_name"]}")"
LEFTMEBER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","left_chat_member","username"]}")"
LEFTMEMBER[ISBOT]="${UPD["result",${num},"message","left_chat_member","is_bot"]}"
MESSAGE[0]="/left_chat_member ${LEFTMEMBER[USERNAME]:=${LEFTMEMBER[FIRST_NAME]} ${LEFTMEMBER[LAST_NAME]}}"
fi
if grep -qs -e '\["result",'"${num}"',"message","\(new_chat_[tp]\)\|\(pinned_message\)' <<<"${UPDATE}"; then
SERVICE[NEWTITLE]="$(JsonDecode "${UPD["result",${num},"message","new_chat_title"]}")"
[ -n "${SERVICE[NEWTITLE]}" ] && MESSAGE[0]="/new_chat_title ${SERVICE[NEWTITLE]}"
SERVICE[NEWPHOTO]="$(get_file "${UPD["result",${num},"message","new_chat_photo",0,"file_id"]}")"
[ -n "${SERVICE[NEWPHOTO]}" ] && MESSAGE[0]="/new_chat_photo ${SERVICE[NEWPHOTO]}"
SERVICE[PINNED]="$(JsonDecode "${UPD["result",${num},"message","pinned_message"]}")"
[ -n "${SERVICE[PINNED]}" ] && MESSAGE[0]="/new_pinned_message ${SERVICE[PINNED]}"
fi fi
SERVICE[LEFTMEMBER]="${UPD["result",${num},"message","left_chat_member","id"]}"
SERVICE[NEWTILE]="${UPD["result",${num},"message","new_chat_title"]}"
SERVICE[NEWPHOTO]="${UPD["result",${num},"message","new_chat_photo"]}"
SERVICE[PINNED]="${UPD["result",${num},"message","pinned_message"]}"
# set SSERVICE to yes if a service message was received # set SSERVICE to yes if a service message was received
[[ "${SERVICE[*]}" =~ ^[[:blank:]]+$ ]] || SERVICE[0]="yes" [[ "${SERVICE[*]}" =~ ^[[:blank:]]*$ ]] || SERVICE[0]="yes"
# split message in command and args # split message in command and args
CMD=( ) [ "${MESSAGE[0]:0:1}" = "/" ] && read -r CMD <<<"${MESSAGE[0]}" && CMD[0]="${CMD[0]%%@*}"
if [[ "${MESSAGE[0]}" == "/"* ]]; then
set -f; unset IFS
# shellcheck disable=SC2206
CMD=( ${MESSAGE[0]} )
CMD[0]="${CMD[0]%%@*}"
set +f
fi
} }
######################### #########################
# main get updates loop, should never terminate # main get updates loop, should never terminate
declare -A BASHBOTBLOCKED
export BASHBOTDEBUG
start_bot() { start_bot() {
<<<<<<< HEAD
local DEBUG="$1" local DEBUG="$1"
local OFFSET=0 local OFFSET=0
# adaptive sleep deafults # adaptive sleep deafults
=======
local ADMIN OFFSET=0
# adaptive sleep defaults
>>>>>>> develop
local nextsleep="100" local nextsleep="100"
local stepsleep="${BASHBOT_SLEEP_STEP:-100}" local stepsleep="${BASHBOT_SLEEP_STEP:-100}"
local maxsleep="${BASHBOT_SLEEP:-5000}" local maxsleep="${BASHBOT_SLEEP:-5000}"
# startup message
BASHBOTDEBUG="$(date): Start BASHBOT updates in Mode \"${1:-normal}\" =========="
printf "%s\n" "${BASHBOTDEBUG}" >>"${UPDATELOG}"
# redirect to Debug.log # redirect to Debug.log
[[ "${DEBUG}" == *"debug" ]] && exec &>>"${LOGDIR}/DEBUG.log" [[ "${1}" == *"debug" ]] && exec &>>"${DEBUGLOG}"
[ -n "${DEBUG}" ] && printf "%s: Start BASHBOT in Mode \"%s\" ==========\n" "$(date)" "${DEBUG}" printf "%s\n" "${BASHBOTDEBUG}"; BASHBOTDEBUG="${1}"
[[ "${DEBUG}" == "xdebug"* ]] && set -x [[ "${BASHBOTDEBUG}" == "xdebug"* ]] && set -x
#cleaup old pipes and empty logfiles #cleaup old pipes and empty logfiles
find "${DATADIR}" -type p -delete find "${DATADIR}" -type p -delete
find "${DATADIR}" -size 0 -name "*.log" -delete find "${DATADIR}" -size 0 -name "*.log" -delete
# load addons on startup # load addons on startup
for addons in "${ADDONDIR:-.}"/*.sh ; do for addons in "${ADDONDIR:-.}"/*.sh ; do
# shellcheck source=./modules/aliases.sh # shellcheck source=./modules/aliases.sh
[ -r "${addons}" ] && source "${addons}" "startbot" "${DEBUG}" [ -r "${addons}" ] && source "${addons}" "startbot" "${BASHBOTDEBUG}"
done done
# shellcheck source=./commands.sh # shellcheck source=./commands.sh
source "${COMMANDS}" "startbot" source "${COMMANDS}" "startbot"
# start timer events # start timer events
if [ -n "${BASHBOT_START_TIMER}" ] ; then if [ -n "${BASHBOT_START_TIMER}" ] ; then
# shellcheck disable=SC2064 # shellcheck disable=SC2064
trap "event_timer $DEBUG" ALRM trap "event_timer $BASHBOTDEBUG" ALRM
start_timer & start_timer &
# shellcheck disable=SC2064 # shellcheck disable=SC2064
trap "kill -9 $!; exit" EXIT INT HUP TERM QUIT trap "kill -9 $!; exit" EXIT INT HUP TERM QUIT
@ -815,18 +886,23 @@ start_bot() {
# cleanup countfile on startup # cleanup countfile on startup
jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" "${COUNTFILE}" jssh_deleteKeyDB "CLEAN_COUNTER_DATABASE_ON_STARTUP" "${COUNTFILE}"
[ -f "${COUNTFILE}.jssh.flock" ] && rm -f "${COUNTFILE}.jssh.flock" [ -f "${COUNTFILE}.jssh.flock" ] && rm -f "${COUNTFILE}.jssh.flock"
jssh_deleteKeyDB "CLEAN_BOT_BOTCONFIG_ON_STARTUP" "${BOTCONFIG}"
[ -f "${BOTCONFIG}.jssh.flock" ] && rm -f "${BOTCONFIG}.jssh.flock"
jssh_readDB_async "BASHBOTBLOCKED" "${BLOCKEDFILE}"
# inform botadmin about start
ADMIN="$(getConfigKey "botadmin")"
[ -n "${ADMIN}" ] && send_normal_message "${ADMIN}" "Bot $(getConfigKey "botname") started ..." &
########## ##########
# bot is ready, start processing updates ... # bot is ready, start processing updates ...
while true; do while true; do
# adaptive sleep in ms rounded to next 0.1 s # adaptive sleep in ms rounded to next 0.1 s
sleep "$(_round_float "${nextsleep}e-3" "1")" sleep "$(_round_float "${nextsleep}e-3" "1")"
# get next update # get next update
UPDATE="$(getJson "$UPD_URL$OFFSET" 2>/dev/null | "${JSONSHFILE}" -s -b -n 2>/dev/null | iconv -f utf-8 -t utf-8 -c)" UPDATE="$(getJson "$UPD_URL$OFFSET" "nolog" 2>/dev/null | "${JSONSHFILE}" -s -b -n 2>/dev/null | iconv -f utf-8 -t utf-8 -c)"
# did we ge an responsn0r # did we ge an responsn0r
if [ -n "${UPDATE}" ]; then if [ -n "${UPDATE}" ]; then
# we got something, do processing # we got something, do processing
[ "${OFFSET}" = "-999" ] && [ "${nextsleep}" -gt "${maxsleep}" ] &&\ [ "${OFFSET}" = "-999" ] && [ "${nextsleep}" -gt "$((maxsleep*2))" ] &&\
printf "%s: Recovered from timeout/broken/no connection, continue with telegram updates\n"\ printf "%s: Recovered from timeout/broken/no connection, continue with telegram updates\n"\
"$(date)" >>"${ERRORLOG}" "$(date)" >>"${ERRORLOG}"
# escape bash $ expansion bug # escape bash $ expansion bug
@ -838,10 +914,10 @@ start_bot() {
if [ "$OFFSET" != "1" ]; then if [ "$OFFSET" != "1" ]; then
nextsleep="100" nextsleep="100"
process_updates "${DEBUG}" process_updates "${BASHBOTDEBUG}"
fi fi
else else
# ups, something bad happend, wait maxsleep*10 # ups, something bad happened, wait maxsleep*10
(( nextsleep=nextsleep*2 , nextsleep= nextsleep>maxsleep*10 ?maxsleep*10:nextsleep )) (( nextsleep=nextsleep*2 , nextsleep= nextsleep>maxsleep*10 ?maxsleep*10:nextsleep ))
[ "${OFFSET}" = "-999" ] &&\ [ "${OFFSET}" = "-999" ] &&\
printf "%s: Repeated timeout/broken/no connection on telegram update, sleep %ds\n"\ printf "%s: Repeated timeout/broken/no connection on telegram update, sleep %ds\n"\
@ -886,14 +962,14 @@ bot_init() {
chmod -R u+w "${COUNTFILE}"* "${BLOCKEDFILE}"* "${DATADIR}" "${BOTADMIN}" "${LOGDIR}/"*.log 2>/dev/null chmod -R u+w "${COUNTFILE}"* "${BLOCKEDFILE}"* "${DATADIR}" "${BOTADMIN}" "${LOGDIR}/"*.log 2>/dev/null
chmod -R o-r,o-w "${COUNTFILE}"* "${BLOCKEDFILE}"* "${DATADIR}" "${TOKENFILE}" "${BOTADMIN}" "${BOTACL}" 2>/dev/null chmod -R o-r,o-w "${COUNTFILE}"* "${BLOCKEDFILE}"* "${DATADIR}" "${TOKENFILE}" "${BOTADMIN}" "${BOTACL}" 2>/dev/null
# jsshDB must writeable by owner # jsshDB must writeable by owner
find . -name '*.jssh' -exec chmod u+w \{\} + find . -name '*.jssh*' -exec chmod u+w \{\} +
fi fi
# show result # show result
ls -l ls -l
} }
if ! _is_function send_message ; then if ! _is_function send_message ; then
echo -e "${RED}ERROR: send_message is not availible, did you deactivate ${MODULEDIR}/sendMessage.sh?${NC}" echo -e "${RED}ERROR: send_message is not available, did you deactivate ${MODULEDIR}/sendMessage.sh?${NC}"
exit 1 exit 1
fi fi
@ -908,54 +984,61 @@ if [ ! -f "${JSONSHFILE}" ]; then
chmod +x "${JSONSHFILE}" chmod +x "${JSONSHFILE}"
fi fi
if [ "${SOURCE}" != "yes" ] && [ "$1" != "init" ] && [ "$1" != "help" ]; then
ME="$(getBotName)"
if [ -z "$ME" ]; then
echo -e "${RED}ERROR: Can't connect to Telegram! Your TOKEN is invalid or you are blocked by ${URL%/*} ...${NC}"
case "$1" in
"" | "stop" | "kill"* | "suspendb"* ) # warn, but do not exit
echo -e "${RED}Ignored to continue for $1 ... ${NC}";;
*) exit 1;;
esac
fi
fi
# source the script with source as param to use functions in other scripts # source the script with source as param to use functions in other scripts
# do not execute if read from other scripts # do not execute if read from other scripts
if [ "${SOURCE}" != "yes" ]; then if [ -z "${SOURCE}" ]; then
############## ##############
# internal options only for use from bashbot and developers # internal options only for use from bashbot and developers
case "$1" in case "$1" in
"outproc") # forward output from interactive and jobs to chat # update botname botname when starting only
[ -z "$3" ] && echo "No file to read from" && exit 3 "botname"|"start"*)
ME="$(getBotName)"
if [ -n "${ME}" ]; then
# ok we have a connection an got botname, save it
[ -n "${CLEAR}" ] && echo -e "${GREY}Bottoken is valid ...${NC}"
jssh_updateKeyDB "botname" "${ME}" "${BOTCONFIG}"
rm -f "${BOTCONFIG}.jssh.flock"
else
echo -e "${GREY}Info: Can't get Botname from Telegram, try cached one ...${NC}"
ME="$(getConfigKey "botname")"
if [ -z "$ME" ]; then
echo -e "${RED}ERROR: No cached botname, can't continue! ...${NC}"
exit 1
fi
fi
[ -n "${CLEAR}" ] && printf "Bot Name: %s\n" "${ME}"
[ "$1" = "botname" ] && exit
;;&
# used to send output of background and interactive to chats
"outproc") # $2 chat_id $3 identifier of job, internal use only!
[ -z "$3" ] && echo "No job identifier" && exit 3
[ -z "$2" ] && echo "No chat to send to" && exit 3 [ -z "$2" ] && echo "No chat to send to" && exit 3
ME="$(getConfigKey "botname")"
# read until terminated
while read -r line ;do while read -r line ;do
[ -n "$line" ] && send_message "$2" "$line" [ -n "$line" ] && send_message "$2" "$line"
done done
# cleanup datadir, keep logfile if not empty
rm -f -r "${DATADIR:-.}/$3" rm -f -r "${DATADIR:-.}/$3"
[ -s "${DATADIR:-.}/$3.log" ] || rm -f "${DATADIR:-.}/$3.log" [ -s "${DATADIR:-.}/$3.log" ] || rm -f "${DATADIR:-.}/$3.log"
exit exit
;; ;;
# finally starts the read update loop, internal use only1
"startbot" ) "startbot" )
start_bot "$2" start_bot "$2"
exit exit
;; ;;
"init") # adjust users and permissions # run after every update to update files and adjust permissions
"init")
bot_init "$2" bot_init "$2"
exit exit
;; ;;
esac # print usage sats
"count") echo -e "${RED}Command ${GREY}count${RED} is deprecated, use ${GREY}stats{$RED}instead.${NC}";&
"stats")
############### ME="$(getConfigKey "botname")"
# "official" arguments as shown to users
SESSION="${ME:-unknown}-startbot"
BOTPID="$(proclist "${SESSION}")"
case "$1" in
"stats"|'count')
declare -A STATS declare -A STATS
jssh_readDB_async "STATS" "${COUNTFILE}" jssh_readDB_async "STATS" "${COUNTFILE}"
for MSG in ${!STATS[*]} for MSG in ${!STATS[*]}
@ -970,17 +1053,19 @@ if [ "${SOURCE}" != "yes" ]; then
echo "A total of ${MESSAGES} messages from ${USERS} users are processed." echo "A total of ${MESSAGES} messages from ${USERS} users are processed."
exit exit
;; ;;
# sedn message to all users
'broadcast') 'broadcast')
ME="$(getConfigKey "botname")"
declare -A SENDALL declare -A SENDALL
shift shift
jssh_readDB_async "SENDALL" "${COUNTFILE}" jssh_readDB_async "SENDALL" "${COUNTFILE}"
echo -e "Sending broadcast message to all users \c" echo -e "Sending broadcast message to all users of ${ME} \c"
for MSG in ${!SENDALL[*]} for MSG in ${!SENDALL[*]}
do do
[[ ! "${MSG}" =~ ^[0-9-]*$ ]] && continue [[ ! "${MSG}" =~ ^[0-9-]*$ ]] && continue
(( USERS++ )) (( USERS++ ))
if [ -n "$*" ]; then if [ -n "$*" ]; then
send_markdown_message "${MSG}" "$*" send_message "${MSG}" "$*"
echo -e ".\c" echo -e ".\c"
sleep 0.1 sleep 0.1
fi fi
@ -988,21 +1073,30 @@ if [ "${SOURCE}" != "yes" ]; then
echo -e "\nMessage \"$*\" sent to ${USERS} users." echo -e "\nMessage \"$*\" sent to ${USERS} users."
exit exit
;; ;;
# does what is says
"status") "status")
ME="$(getConfigKey "botname")"
SESSION="${ME:-_bot}-startbot"
BOTPID="$(proclist "${SESSION}")"
if [ -n "${BOTPID}" ]; then if [ -n "${BOTPID}" ]; then
echo -e "${GREEN}Bot is running.${NC}" echo -e "${GREEN}Bot is running with UID ${RUNUSER}.${NC}"
exit exit
else else
echo -e "${ORANGE}Bot not running.${NC}" echo -e "${ORANGE}No Bot running with UID ${RUNUSER}.${NC}"
exit 5 exit 5
fi fi
;; ;;
# start bot as background jod and check if bot is running
"start") "start")
# shellcheck disable=SC2086
SESSION="${ME:-_bot}-startbot"
BOTPID="$(proclist "${SESSION}")"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
[ -n "${BOTPID}" ] && kill ${BOTPID} [ -n "${BOTPID}" ] && kill ${BOTPID}
nohup "$SCRIPT" "startbot" "$2" "${SESSION}" &>/dev/null & nohup "$SCRIPT" "startbot" "$2" "${SESSION}" &>/dev/null &
echo "Session Name: ${SESSION}" printf "Session Name: %s\n" "${SESSION}"
sleep 1
if [ -n "$(proclist "${SESSION}")" ]; then if [ -n "$(proclist "${SESSION}")" ]; then
echo -e "${GREEN}Bot started successfully.${NC}" echo -e "${GREEN}Bot started successfully.${NC}"
else else
@ -1010,29 +1104,37 @@ if [ "${SOURCE}" != "yes" ]; then
exit 5 exit 5
fi fi
;; ;;
"kill"|"stop") # does what it says
"kill") echo -e "${RED}Command ${GREY}kill${RED} is deprecated, use ${GREY}stop{$RED}instead.${NC}";&
"stop")
ME="$(getConfigKey "botname")"
SESSION="${ME:-_bot}-startbot"
BOTPID="$(proclist "${SESSION}")"
if [ -n "${BOTPID}" ]; then if [ -n "${BOTPID}" ]; then
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if kill ${BOTPID}; then if kill ${BOTPID}; then
# inform botadmin about stop
ADMIN="$(getConfigKey "botadmin")"
[ -n "${ADMIN}" ] && send_normal_message "${ADMIN}" "Bot ${ME} stopped ..." &
echo -e "${GREEN}OK. Bot stopped successfully.${NC}" echo -e "${GREEN}OK. Bot stopped successfully.${NC}"
else else
echo -e "${RED}An error occured while stopping bot.${NC}" echo -e "${RED}An error occurred while stopping bot.${NC}"
exit 5 exit 5
fi fi
else
echo -e "${ORANGE}No Bot running with UID ${RUNUSER}.${NC}"
fi fi
exit exit
;; ;;
"resumeb"* | "killb"* | "suspendb"*) # suspend, resume or kill background jobs
_is_function job_control || { echo -e "${RED}Module background is not availible!${NC}"; exit 3; } "suspendb"*|"resumeb"*|"killb"*)
_is_function job_control || { echo -e "${RED}Module background is not available!${NC}"; exit 3; }
ME="$(getConfigKey "botname")"
job_control "$1" job_control "$1"
;; ;;
"help")
less "README.txt"
exit
;;
*) *)
echo -e "${RED}${REALME}: BAD REQUEST${NC}" echo -e "${RED}${REALME##*/}: unknown command${NC}"
echo -e "${ORANGE}Available arguments: ${GREY}start, stop, kill, status, stats, broadcast, help, suspendback, resumeback, killback${NC}" echo -e "${RED}Available commands: ${GREY}${BOTCOMMANDS}${NC}" && exit
exit 4 exit 4
;; ;;
esac esac

View File

@ -8,14 +8,14 @@
# | |__/ / |_| | | | | | |_| | |__ | |____( (_| | | |__ _ # | |__/ / |_| | | | | | |_| | |__ | |____( (_| | | |__ _
# |_____/ \___/ |_| |_|\___/ \___) |_______)____|_|\___)_| # |_____/ \___/ |_| |_|\___/ \___) |_______)____|_|\___)_|
# #
# this file *MUST* not be edited! palce your config and commands in # this file *MUST* not be edited! place your config and commands in
# the file "mycommnds.sh". a clean version is provided as "mycommands.clean" # the file "mycommnds.sh". a clean version is provided as "mycommands.clean"
# #
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# #
# adjust your language setting here, e.g.when run from other user or cron. # adjust your language setting here, e.g.when run from other user or cron.
@ -83,7 +83,7 @@ if [ -z "${1}" ] || [[ "${1}" == *"debug"* ]];then
# forward iinline query to optional dispatcher # forward iinline query to optional dispatcher
_exec_if_function myinlines _exec_if_function myinlines
# regular (gobal) commands ... # regular (global) commands ...
# your commands are in mycommands() # your commands are in mycommands()
else else
@ -128,7 +128,7 @@ if [ -z "${1}" ] || [[ "${1}" == *"debug"* ]];then
unban_chat_member "${CHAT[ID]}" "${USER[ID]}" unban_chat_member "${CHAT[ID]}" "${USER[ID]}"
;; ;;
'/'*) # discard all unkown commands '/'*) # discard all unknown commands
: ;; : ;;
*) # forward message to interactive chats *) # forward message to interactive chats
_exec_if_function send_interactive "${CHAT[ID]}" "${MESSAGE}" _exec_if_function send_interactive "${CHAT[ID]}" "${MESSAGE}"

View File

@ -3,7 +3,7 @@
{ {
"ok":true, "ok":true,
"url":"https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/", "url":"https://my-json-server.typicode.com/topkecleon/telegram-bot-bash/",
"description":"Testing of JSON reponses for github.com/topkecleon/telegram-bot-bash" "description":"Testing of JSON responses for github.com/topkecleon/telegram-bot-bash"
} , } ,
"false": "false":
{ {

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-pre-0-g03700cd
############ ############
# NOTE: you MUST run install-hooks.sh again when updating this file! # NOTE: you MUST run install-hooks.sh again when updating this file!
@ -20,5 +20,15 @@ echo "............................"
unset IFS; set -f unset IFS; set -f
if which codespell &>/dev/null; then
echo "Running codespell"
echo "............................"
codespell -B 1 --skip="*.log,*.html,*.txt,.git*" -L "ba"
echo "if there are (to many) typo's shown, consider running:"
echo "codespell -i 3 -w --skip=\"*.log,*.html,*.txt,.git*\" -L \"ba\""
else
echo "consider installing codespell: pip install codespell"
fi
echo "............................"
# note date of last push for version # note date of last push for version
touch "${LASTPUSH}" touch "${LASTPUSH}"

View File

@ -2,7 +2,7 @@
# file: make-distribution.sh # file: make-distribution.sh
# creates files and arcchives to dirtribute bashbot # creates files and arcchives to dirtribute bashbot
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-64-gcac1ea4
# magic to ensure that we're always inside the root of our application, # magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script # no matter from which directory we'll run script
@ -17,7 +17,7 @@ VERSION="$(git describe --tags | sed -e 's/-[0-9].*//' -e 's/v//')"
DISTNAME="telegram-bot-bash" DISTNAME="telegram-bot-bash"
DISTDIR="./DIST/${DISTNAME}" DISTDIR="./DIST/${DISTNAME}"
DISTFILES="bashbot.rc bashbot.sh commands.sh mycommands.sh mycommands.sh.clean doc examples modules addons LICENSE README.md README.txt README.html" DISTFILES="bashbot.rc bashbot.sh commands.sh mycommands.sh mycommands.sh.clean doc examples logs scripts modules addons LICENSE README.md README.txt README.html"
# run tests first! # run tests first!
@ -33,10 +33,13 @@ done
# create dir for distribution and copy files # create dir for distribution and copy files
mkdir -p "${DISTDIR}" 2>/dev/null mkdir -p "${DISTDIR}" 2>/dev/null
# shellcheck disable=SC2086 # shellcheck disable=SC2086
echo "Copy files"
# shellcheck disable=SC2086
cp -r ${DISTFILES} "${DISTDIR}" cp -r ${DISTFILES} "${DISTDIR}"
cd "${DISTDIR}" || exit 1 cd "${DISTDIR}" || exit 1
# do not overwrite on update # do not overwrite on update
echo "Create .dist files"
for file in mycommands.sh bashbot.rc addons/*.sh for file in mycommands.sh bashbot.rc addons/*.sh
do do
[ "${file}" = "addons/*.sh" ] && continue [ "${file}" = "addons/*.sh" ] && continue
@ -44,6 +47,7 @@ do
done done
# dwonload JSON.sh # dwonload JSON.sh
echo "Inject JSON.sh"
JSONSHFILE="JSON.sh/JSON.sh" JSONSHFILE="JSON.sh/JSON.sh"
if [ ! -f "${JSONSHFILE}" ]; then if [ ! -f "${JSONSHFILE}" ]; then
mkdir "JSON.sh" 2>/dev/null mkdir "JSON.sh" 2>/dev/null
@ -52,17 +56,17 @@ if [ ! -f "${JSONSHFILE}" ]; then
fi fi
# make html doc # make html doc
mkdir html 2>/dev/null echo "Create html doc"
cp README.html html/index.html #shellcheck disable=SC1090
find doc -iname "*.md" -type f -exec sh -c 'pandoc -s -f commonmark -M "title=Bashobot Documentation - ${0%.md}.html" "${0}" -o "./html/$(basename ${0%.md}.html)"' {} \; source "$GIT_DIR/../dev/make-html.sh"
find examples -iname "*.md" -type f -exec sh -c 'pandoc -s -f commonmark -M "title=Bashobot Documentation - ${0%.md}.html" "${0}" -o "${0%.md}.html"' {} \;
find README.html html examples -iname "*.html" -type f -exec sh -c 'sed -i -E "s/href=\"(\.\.\/)*doc\//href=\"\1html\//g;s/href=\"(.*).md(#.*)*\"/href=\"\1.html\"/g" ${0}' {} \;
# create archive # create archive
cd .. || exit 1 cd .. || exit 1
echo "Create dist archives"
zip -rq "${DISTNAME}-${VERSION}.zip" "${DISTNAME}" zip -rq "${DISTNAME}-${VERSION}.zip" "${DISTNAME}"
tar -czf "${DISTNAME}-${VERSION}.tar.gz" "${DISTNAME}" tar -czf "${DISTNAME}-${VERSION}.tar.gz" "${DISTNAME}"
echo "Done!"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
ls -ld ${DISTNAME}-${VERSION}.* ls -ld ${DISTNAME}-${VERSION}.*

13
dev/make-html.sh Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script
if [ ! -f README.html ]; then
echo "This script must run where README.html is!" && exit 1
fi
# make html doc
mkdir html 2>/dev/null
cp README.html html/index.html
find doc -iname "*.md" -type f -exec sh -c 'pandoc -s -f commonmark -M "title=Bashobot Documentation - ${0%.md}.html" "${0}" -o "./html/$(basename ${0%.md}.html)"' {} \;
find examples -iname "*.md" -type f -exec sh -c 'pandoc -s -f commonmark -M "title=Bashobot Documentation - ${0%.md}.html" "${0}" -o "${0%.md}.html"' {} \;
find README.html html examples -iname "*.html" -type f -exec sh -c 'sed -i -E "s/href=\"(\.\.\/)*doc\//href=\"\1html\//g;s/href=\"(.*).md(#.*)*\"/href=\"\1.html\"/g" ${0}' {} \;

View File

@ -5,7 +5,7 @@
# If you your bot is finished you can use make-standalone.sh to create the # If you your bot is finished you can use make-standalone.sh to create the
# the old all-in-one bashbot: bashbot.sh and commands.sh only! # the old all-in-one bashbot: bashbot.sh and commands.sh only!
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# magic to ensure that we're always inside the root of our application, # magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script # no matter from which directory we'll run script
@ -18,7 +18,7 @@ fi
#DISTNAME="telegram-bot-bash" #DISTNAME="telegram-bot-bash"
DISTDIR="./STANDALONE/${DISTNAME}" DISTDIR="./STANDALONE/${DISTNAME}"
DISTFILES="bashbot.sh bashbot.rc commands.sh mycommands.sh dev/obfuscate.sh modules LICENSE README.txt token count botacl botadmin" DISTFILES="bashbot.sh bashbot.rc commands.sh mycommands.sh dev/obfuscate.sh modules scripts logs LICENSE README.* doc botacl botconfig.jssh"
# run pre_commit on files # run pre_commit on files
dev/hooks/pre-commit.sh dev/hooks/pre-commit.sh
@ -38,7 +38,7 @@ echo " ... create unified commands.sh"
{ {
# first head of commands.sh # first head of commands.sh
sed -n '0,/^if / p' commands.sh | head -n -2 | grep -v 'mycommands.sh' sed -n '0,/^if / p' commands.sh | head -n -2
# then mycommands from first non comment line on # then mycommands from first non comment line on
printf '\n##############################\n# my commands starts here ...\n' printf '\n##############################\n# my commands starts here ...\n'
@ -46,7 +46,7 @@ echo " ... create unified commands.sh"
# last tail of commands.sh # last tail of commands.sh
printf '\n##############################\n# default commands starts here ...\n' printf '\n##############################\n# default commands starts here ...\n'
sed -n '/\/mycommands.sh"/,$ p' commands.sh | tail -n +2 sed -n '/source .*\/mycommands.sh"/,$ p' commands.sh | tail -n +2
} >>$$commands.sh } >>$$commands.sh
@ -79,10 +79,15 @@ sed -E -e '/(shellcheck)|(#!\/bin\/bash)/! s/^[[:space:]]*#.*//' -e 's/^[[:space
sed -E -e '/(shellcheck)|(#!\/bin\/bash)/! s/^[[:space:]]*#.*//' -e 's/^[[:space:]]*//' -e '/^$/d' commands.sh > commands.sh.min sed -E -e '/(shellcheck)|(#!\/bin\/bash)/! s/^[[:space:]]*#.*//' -e 's/^[[:space:]]*//' -e '/^$/d' commands.sh > commands.sh.min
chmod +x bashbot.sh.min chmod +x bashbot.sh.min
# make html doc
echo "Create html doc"
#shellcheck disable=SC1090
source "$GIT_DIR/../dev/make-html.sh"
echo "Done!" echo "Done!"
cd .. || exit 1 cd .. || exit 1
echo -e "\\nStandalone bashbot files are now availible in \"${DISTDIR}\":\\n" echo -e "\\nStandalone bashbot files are now available in \"${DISTDIR}\":\\n"
ls -l "${DISTDIR}"* ls -l "${DISTDIR}"

View File

@ -24,7 +24,7 @@ As an alternative to download the zip files, you can clone the github repository
* /usr/local if you want to run as service * /usr/local if you want to run as service
2. Run ```git clone https://github.com/topkecleon/telegram-bot-bash.git``` 2. Run ```git clone https://github.com/topkecleon/telegram-bot-bash.git```
3. Change into the directory ```telegram-bot-bash``` 3. Change into the directory ```telegram-bot-bash```
4. Run ``` test/ALL-tests.sh``` and if everthing finish OK ... 4. Run ``` test/ALL-tests.sh``` and if everything finish OK ...
5. Run ```sudo ./bashbot.sh init``` to setup the environment and enter your Bots token given by botfather. 5. Run ```sudo ./bashbot.sh init``` to setup the environment and enter your Bots token given by botfather.
### Update bashbot ### Update bashbot
@ -51,6 +51,12 @@ see e.g. [Install Bash on Mac](http://macappstore.org/bash/)
**On BSD and MacOS** I recommend to install gnu coreutils and include them in front of your PATH **On BSD and MacOS** I recommend to install gnu coreutils and include them in front of your PATH
environment variable before running bashbot, e.g. the gnu versions of sed, grep, find ... environment variable before running bashbot, e.g. the gnu versions of sed, grep, find ...
I considered to make bashbot BSD sed compatible, but much of the bashbot "magic" relies on
(gnu) sed features, e.g. alternation ```|```, non printables ```\n\t\<``` or repeat ```?+``` pattern, not supported by BSD sed.
BSD/MacOS sed compatibility will result in a rewrite of all grep/sed commands with an uncertain outcome,
see [BSD/MacOS vs. GNU sed](https://riptutorial.com/sed/topic/9436/bsd-macos-sed-vs--gnu-sed-vs--the-posix-sed-specification)
to get an impression how different they are.
In adition you must adjust the shebang line of the scripts ```bashbot.sh``` and ```json.sh``` to point to to the correct bash In adition you must adjust the shebang line of the scripts ```bashbot.sh``` and ```json.sh``` to point to to the correct bash
or use the example script: ```examples/bash2env *.sh */*.sh``` or use the example script: ```examples/bash2env *.sh */*.sh```
@ -61,7 +67,7 @@ Bashbot will stay with /bin/bash shebang, as using a fixed path is more secure t
#### removal of TMUX #### removal of TMUX
From version 0.80 on TMUX is no longer needed and the bachsbot command 'attach' is deleted. Old function 'inproc' From version 0.80 on TMUX is no longer needed and the bachsbot command 'attach' is deleted. Old function 'inproc'
is replaced by 'send_interactive'. send_interactive does checks if an interactive job is running internaly. is replaced by 'send_interactive'. send_interactive does checks if an interactive job is running internally.
Pls check if you make use of inproc and remove it including the old checks, e.g. Pls check if you make use of inproc and remove it including the old checks, e.g.
```bash ```bash
if tmux ls | grep -v send | grep -q "$copname"; then inproc; fi if tmux ls | grep -v send | grep -q "$copname"; then inproc; fi
@ -82,8 +88,8 @@ From version 0.70 on the tmp dir is renamed to 'data-bot-bash' to reflect the fa
From version 0.50 on the temporary files are no more placed in '/tmp'. instead a dedicated tmp dir is used. From version 0.50 on the temporary files are no more placed in '/tmp'. instead a dedicated tmp dir is used.
#### Changes to send_keyboard in v0.6 #### Changes to send_keyboard in v0.6
From Version 0.60 on keybord format for ```send_keyboard``` and ```send_message "mykeyboardstartshere ..."``` was changed. From Version 0.60 on keyboard format for ```send_keyboard``` and ```send_message "mykeyboardstartshere ..."``` was changed.
Keybords are now defined in JSON Array notation e.g. "[ \\"yes\\" , \\"no\\" ]". Keyboards are now defined in JSON Array notation e.g. "[ \\"yes\\" , \\"no\\" ]".
This has the advantage that you can create any type of keyboard supported by Telegram. This has the advantage that you can create any type of keyboard supported by Telegram.
The old format is supported for backward compatibility, but may fail for corner cases. The old format is supported for backward compatibility, but may fail for corner cases.
@ -101,5 +107,5 @@ The old format is supported for backward compatibility, but may fail for corner
#### [Next Create Bot](1_firstbot.md) #### [Next Create Bot](1_firstbot.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -1,5 +1,5 @@
#### [Home](../README.md) #### [Home](../README.md)
## Gettting Started ## Getting Started
The Bots standard commands are in the commands dispatcher ```commands.sh```, Do not edit this file! Add your commands and functions to ```mycommands.sh```. In 'mycommands.sh.dist' you find examples how to add own commands and overwrite existing ones. See [Best practices](5_practice.md) for more information. The Bots standard commands are in the commands dispatcher ```commands.sh```, Do not edit this file! Add your commands and functions to ```mycommands.sh```. In 'mycommands.sh.dist' you find examples how to add own commands and overwrite existing ones. See [Best practices](5_practice.md) for more information.
@ -19,7 +19,7 @@ Have FUN!
. .
├── mycommands.sh # THIS is your bot, place logic and commands here! ├── mycommands.sh # THIS is your bot, place logic and commands here!
├── mycommands.sh.clean # copy to "mycommands.sh" if you start devloping your bot ├── mycommands.sh.clean # copy to "mycommands.sh" if you start developing your bot
├── mycommands.sh.dist # example bot, also used for testing bashbot internally ├── mycommands.sh.dist # example bot, also used for testing bashbot internally
├── count.jssh # count bashbot usage in jssh key-value store ├── count.jssh # count bashbot usage in jssh key-value store
@ -32,17 +32,17 @@ Have FUN!
├── scripts # place your bashbot interactive and background scripts here ├── scripts # place your bashbot interactive and background scripts here
│   └── interactive.sh.clean # interactive script template for new scripts │   └── interactive.sh.clean # interactive script template for new scripts
├── logs # here you'll find ERROR, DEBUG and MESSAGE.log ├── logs # here you'll find BASHBOT, ERROR, DEBUG and MESSAGE.log
├── modules # optional functions, sourced by commands.sh ├── modules # optional functions, sourced by commands.sh
│   ├── aliases.sh # to disable modules rename them xxx.sh.off │   ├── aliases.sh # to disable modules rename them xxx.sh.off
│   ├── answerInline.sh │   ├── answerInline.sh
│   ├── jsshDB.sh # read and store JSON.sh stlye JSON, mandatory │   ├── jsshDB.sh # read and store JSON.sh style JSON, mandatory
│   ├── background.sh # interactive and background functions │   ├── background.sh # interactive and background functions
│   ├── chatMember.sh │   ├── chatMember.sh
│   └── sendMessage.sh # main send message functions, mandatory │   └── sendMessage.sh # main send message functions, mandatory
├── addons # optional addons, disbaled by default ├── addons # optional addons, disabled by default
│   ├── example.sh # to enable addons change their XXX_ENABLE to true │   ├── example.sh # to enable addons change their XXX_ENABLE to true
│   ├── antiFlood.sh # simple addon taking actions based on # files and text sent to chat │   ├── antiFlood.sh # simple addon taking actions based on # files and text sent to chat
│   └── xxxxxage.sh │   └── xxxxxage.sh
@ -95,15 +95,15 @@ To send a broadcast to all of users that ever used the bot run the following com
---- ----
## Recieve data ## Receive data
Evertime a Message is received, you can read incoming data using the following variables: Evertime a Message is received, you can read incoming data using the following variables:
### Regular Messages ### Regular Messages
* ```${MESSAGE}```: Current message These Variables are always present in regular messages:
* ```${MESSAGE}```: Current message text
* ```${MESSAGE[ID]}```: ID of current message * ```${MESSAGE[ID]}```: ID of current message
* ```$CAPTION```: Captions
* ```$REPLYTO```: Original message wich was replied to
* ```$USER```: This array contains the First name, last name, username and user id of the sender of the current message. * ```$USER```: This array contains the First name, last name, username and user id of the sender of the current message.
* ```${USER[ID]}```: User id * ```${USER[ID]}```: User id
* ```${USER[FIRST_NAME]}```: User's first name * ```${USER[FIRST_NAME]}```: User's first name
@ -117,8 +117,12 @@ Evertime a Message is received, you can read incoming data using the following v
* ```${CHAT[TITLE]}```: Title * ```${CHAT[TITLE]}```: Title
* ```${CHAT[TYPE]}```: Type * ```${CHAT[TYPE]}```: Type
* ```${CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]}```: All members are administrators (true if true) * ```${CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]}```: All members are administrators (true if true)
The following variables are set if the message contains optional parts:
* ```$REPLYTO```: Original message which was replied to
* ```$REPLYTO```: This array contains the First name, last name, username and user id of the ORIGINAL sender of the message REPLIED to. * ```$REPLYTO```: This array contains the First name, last name, username and user id of the ORIGINAL sender of the message REPLIED to.
* ```${REPLYTO[ID]}```: ID of message wich was replied to * ```${REPLYTO[ID]}```: ID of message which was replied to
* ```${REPLYTO[UID]}```: Original user's id * ```${REPLYTO[UID]}```: Original user's id
* ```${REPLYTO[FIRST_NAME]}```: Original user's first name * ```${REPLYTO[FIRST_NAME]}```: Original user's first name
* ```${REPLYTO[LAST_NAME]}```: Original user's' last name * ```${REPLYTO[LAST_NAME]}```: Original user's' last name
@ -129,6 +133,7 @@ Evertime a Message is received, you can read incoming data using the following v
* ```${FORWARD[FIRST_NAME]}```: Original user's first name * ```${FORWARD[FIRST_NAME]}```: Original user's first name
* ```${FORWARD[LAST_NAME]}```: Original user's' last name * ```${FORWARD[LAST_NAME]}```: Original user's' last name
* ```${FORWARD[USERNAME]}```: Original user's username * ```${FORWARD[USERNAME]}```: Original user's username
* ```$CAPTION```: Picture, Audio, Video, File Captions
* ```$URLS```: This array contains documents, audio files, voice recordings and stickers as URL. * ```$URLS```: This array contains documents, audio files, voice recordings and stickers as URL.
* ```${URLS[AUDIO]}```: Audio files * ```${URLS[AUDIO]}```: Audio files
* ```${URLS[VIDEO]}```: Videos * ```${URLS[VIDEO]}```: Videos
@ -151,25 +156,45 @@ Evertime a Message is received, you can read incoming data using the following v
* ```${VENUE[LONGITUDE]}```: Longitude * ```${VENUE[LONGITUDE]}```: Longitude
* ```${VENUE[LATITUDE]}```: Latitude * ```${VENUE[LATITUDE]}```: Latitude
* ```${VENUE[FOURSQUARE]}```: Fouresquare ID * ```${VENUE[FOURSQUARE]}```: Fouresquare ID
* ```$SERVICE```: This array contains info abbout recived service messages.
* ```${SERVICE}```: set to "yes" when a service message is recived.
### Service Messages
Service Messages are regular messages not itended for end users, instead they signal special events to the
client, e.g. new users.
If a service message is received bashbot sets MESSAGE to the service message type as a command,
e.g. if a new user joins a chat MESSAGE is set to "/new_chat_user".
* ```$SERVICE```: This array contains info about received service messages.
* ```${SERVICE}```: "yes" if service message is received
* ```${SERVICE[NEWMEMBER]```: New user's id * ```${SERVICE[NEWMEMBER]```: New user's id
* ```${MESSAGE}```: /new_chat_member
* ```${NEWMEMBER[ID]```: New user's id * ```${NEWMEMBER[ID]```: New user's id
* ```${NEWMEMBER[FIRST_NAME]```: New user's first name * ```${NEWMEMBER[FIRST_NAME]```: New user's first name
* ```${NEWMEMBER[LAST_NAME]```: New user's last name * ```${NEWMEMBER[LAST_NAME]```: New user's last name
* ```${NEWMEMBER[USERNAME]```: New user's username * ```${NEWMEMBER[USERNAME]```: New user's username
* ```${NEWMEMBER[ISBOT]```: New user is a bot * ```${NEWMEMBER[ISBOT]```: New user is a bot
* ```${SERVICE[LEFTMEMBER]```: Id of user left * ```${SERVICE[LEFTMEMBER]```: Id of user left
* ```${SERVICE[NEWTILE]```: Text of new title * ```${MESSAGE}```: /left_chat_member
* ```${SERVICE[NEWPHOTO]```: New Chat Picture array * ```${LEFTMEMBER[ID]```: Left user's id
* ```${LEFTMEMBER[FIRST_NAME]```: Left user's first name
* ```${LEFTMEMBER[LAST_NAME]```: Left user's last name
* ```${LEFTMEMBER[USERNAME]```: Left user's username
* ```${LEFTMEMBER[ISBOT]```: Left user is a bot
* ```${SERVICE[NEWTITLE]```: Text of new title
* ```${MESSAGE}```: /new_chat_title
* ```${SERVICE[NEWPHOTO]```: New Chat Picture URL
* ```${MESSAGE}```: /new_chat_picture
* ```${SERVICE[PINNED]```: Pinned Message structure * ```${SERVICE[PINNED]```: Pinned Message structure
* ```${MESSAGE}```: /new_pinned_message
### Inline query messages
Inline query messages are small, non regular messages used for interaction with the user,
### Inline queries they contain the following variables only:
Evertime a Message is received, you can read incoming data using the following variables:
* ```${iQUERY}```: Current inline query * ```${iQUERY}```: Current inline query
* ```$iQUERY```: This array contains the ID, First name, last name, username and user id of the sender of the current inline query. * ```$iQUERY```: This array contains the ID, First name, last name, username and user id of the sender of the current inline query.
@ -235,8 +260,8 @@ send_file "${CHAT[ID]}" "/home/user/doge.jpg" "Lool"
``` ```
To send custom keyboards use the ```send_keyboard``` function: To send custom keyboards use the ```send_keyboard``` function:
```bash ```bash
send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" '[ "Yep" , "No" ]' # note the simgle quotes! send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" '[ "Yep" , "No" ]' # note the single quotes!
send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" "[ \\"Yep\\" , \\"No\\" ]" # within double quotes you must excape the inside double quots send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" "[ \\"Yep\\" , \\"No\\" ]" # within double quotes you must escape the inside double quots
``` ```
To send locations use the ```send_location``` function: To send locations use the ```send_location``` function:
```bash ```bash
@ -256,5 +281,5 @@ send_action "${CHAT[ID]}" "action"
#### [Prev Create Bot](1_firstbot.md) #### [Prev Create Bot](1_firstbot.md)
#### [Next Advanced Usage](3_advanced.md) #### [Next Advanced Usage](3_advanced.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -29,7 +29,7 @@ In addition you can check individual capabilities of users as you must define in
# a user not listed here, will return false from 'user_is_allowed' # a user not listed here, will return false from 'user_is_allowed'
# #
# Format: # Format:
# user:ressource:chat # user:resource:chat
# allow user 123456789 access to all resources in all chats # allow user 123456789 access to all resources in all chats
123456789:*:* 123456789:*:*
@ -41,7 +41,7 @@ In addition you can check individual capabilities of users as you must define in
987654321:start:98979695 987654321:start:98979695
# * are only allowed on the right hand side and not for user! # * are only allowed on the right hand side and not for user!
# the following exaples are NOT valid! # the following examples are NOT valid!
*:*:* *:*:*
*:start:* *:start:*
*:*:98979695 *:*:98979695
@ -66,7 +66,7 @@ You must use the function ```user_is_allowed``` to check if a user has the capab
### Interactive Chats ### Interactive Chats
Interactive chats are short running scripts, reading user input and echo data to the user. Interactive chats are short running scripts, reading user input and echo data to the user.
To create a new interactive chat script copy 'scripts/interactive.sh.clean' to e.g. 'scripts/mynewinteractive.sh', make it executeable To create a new interactive chat script copy 'scripts/interactive.sh.clean' to e.g. 'scripts/mynewinteractive.sh', make it executable
and then use 'start_proc' function from your bot, it's possible to pass two arguments. You find more examples for interactive scripts in 'examples' and then use 'start_proc' function from your bot, it's possible to pass two arguments. You find more examples for interactive scripts in 'examples'
*usage*: start_proc chat_id script arg1 arg2 *usage*: start_proc chat_id script arg1 arg2
@ -82,7 +82,7 @@ and then use 'start_proc' function from your bot, it's possible to pass two argu
###### ######
# parameters # parameters
# $1 $2 args as given to start_proc chat srcipt arg1 arg2 # $1 $2 args as given to start_proc chat script arg1 arg2
# $3 path to named pipe # $3 path to named pipe
####################### #######################
@ -110,7 +110,7 @@ echo "Text that will appear in chat? myfilelocationstartshere /home/user/doge.jp
``` ```
And buttons: And buttons:
```bash ```bash
echo "Text that will appear in chat. mybtextstartshere Klick me myburlstartshere https://dealz.rrr.de" echo "Text that will appear in chat. mybtextstartshere Click me myburlstartshere https://dealz.rrr.de"
``` ```
And locations: And locations:
```bash ```bash
@ -144,7 +144,7 @@ echo "$out"
A background job is similar to an interactive chat, but can be a long running job and does only output massages, user input is ignored. A background job is similar to an interactive chat, but can be a long running job and does only output massages, user input is ignored.
It's possible to run multiple background jobs from the same chat. It's possible to run multiple background jobs from the same chat.
To create a new interactive chat script copy 'scripts/interactive.sh.clean' to e.g. 'scripts/mynewbackground.sh', make it executeable To create a new interactive chat script copy 'scripts/interactive.sh.clean' to e.g. 'scripts/mynewbackground.sh', make it executable
and then use 'start_back' function from your bot, it's possible to pass two arguments. You find more examples for background scripts in 'examples' and then use 'start_back' function from your bot, it's possible to pass two arguments. You find more examples for background scripts in 'examples'
*usage*: start_back chat_id script jobname arg1 arg2 *usage*: start_back chat_id script jobname arg1 arg2
@ -166,21 +166,21 @@ You can also suspend and resume currently running background jobs from outside b
./bashbot.sh resumeback ./bashbot.sh resumeback
``` ```
If you want to kill all background jobs permantly run: If you want to kill all background jobs permanently run:
```bash ```bash
./bashbot.sh killback ./bashbot.sh killback
``` ```
Note: Background jobs run independent from main bot and continue running until your script exits or you stop it. Backgound jobs will continue running if your Bot is stoped and must be terminated seperatly e.g. by ```bashbot.sh killback``` Note: Background jobs run independent from main bot and continue running until your script exits or you stop it. Background jobs will continue running if your Bot is stopped and must be terminated separately e.g. by ```bashbot.sh killback```
### Inline queries ### Inline queries
**Inline queries** allow users to send commands to your bot from every chat without going to a private chat. An inline query is started if the user type the bots name, e.g. @myBot. Everything after @myBot is immediatly send to the bot. **Inline queries** allow users to send commands to your bot from every chat without going to a private chat. An inline query is started if the user type the bots name, e.g. @myBot. Everything after @myBot is immediately send to the bot.
In order to enable **inline mode**, send `/setinline` command to [@BotFather](https://telegram.me/botfather) and provide the placeholder text that the user will see in the input field after typing your bots name. In order to enable **inline mode**, send `/setinline` command to [@BotFather](https://telegram.me/botfather) and provide the placeholder text that the user will see in the input field after typing your bots name.
The following commands allows you to send ansers to *inline queries*. To enable bashbot to process inline queries set ```INLINE="1"``` in 'mycommands.sh'. The following commands allows you to send ansers to *inline queries*. To enable bashbot to process inline queries set ```INLINE="1"``` in 'mycommands.sh'.
To send messsages or links through an *inline query*: To send messages or links through an *inline query*:
```bash ```bash
answer_inline_query "${iQUERY[ID]}" "article" "Title of the result" "Content of the message to be sent" answer_inline_query "${iQUERY[ID]}" "article" "Title of the result" "Content of the message to be sent"
``` ```
@ -223,10 +223,10 @@ See also [answer_inline_multi, answer_inline_compose](6_reference.md#answer_inli
Our examples usually do not care about errors happening while sending a message, this is OK as long your bot does not send an Our examples usually do not care about errors happening while sending a message, this is OK as long your bot does not send an
massive aoumnt of messages. By default bashbot detects if a message is not sent and try to recover when possible, massive aoumnt of messages. By default bashbot detects if a message is not sent and try to recover when possible,
e.g. resend on throttling. In addtion every send error is logged in logs/ERROR.log e.g. resend on throttling. In addition every send error is logged in logs/ERROR.log
#### Trasmission results #### Transmission results
On every message send to telegram (transmission) the results are provided in bash variables, like its done when a new message On every message send to telegram (transmission) the results are provided in bash variables, like its done when a new message
is received. is received.
@ -236,7 +236,7 @@ every send action will overwrite them!
* ```$BOTSENT```: This array contains the parsed results from the last transmission to telegram. * ```$BOTSENT```: This array contains the parsed results from the last transmission to telegram.
* ```${BOTSENT[OK]}```: contains the string ```true```: after a successful transmission * ```${BOTSENT[OK]}```: contains the string ```true```: after a successful transmission
* ```${BOTSENT[ERROR]}```: Error code if an error occured * ```${BOTSENT[ERROR]}```: Error code if an error occurred
* ```${BOTSENT[DESC]}```: Description text for error * ```${BOTSENT[DESC]}```: Description text for error
* ```${BOTSENT[RETRY]}```: Seconds to wait if telegram requests throtteling. * ```${BOTSENT[RETRY]}```: Seconds to wait if telegram requests throtteling.
* ```$res```: temporary variable containing the full transmission result, may be overwritten by any bashbot function. * ```$res```: temporary variable containing the full transmission result, may be overwritten by any bashbot function.
@ -279,7 +279,7 @@ Note: If you disable automatic retry, se above, you disable also connection prob
# may be we removed block, e.g. changed IP address, try again # may be we removed block, e.g. changed IP address, try again
return 0 return 0
fi fi
# do not retry if we cant recover # do not retry if we can't recover
return 1 return 1
} }
@ -289,5 +289,5 @@ Note: If you disable automatic retry, se above, you disable also connection prob
#### [Prev Getting started](2_usage.md) #### [Prev Getting started](2_usage.md)
#### [Next Expert Use](4_expert.md) #### [Next Expert Use](4_expert.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -33,9 +33,9 @@ export 'LC_ALL=en_US.UTF-8'
export 'LANG=de_en_US.UTF-8' export 'LANG=de_en_US.UTF-8'
export 'LANGUAGE=den_US.UTF-8' export 'LANGUAGE=den_US.UTF-8'
``` ```
3. make shure your bot scripts use the correct settings, eg. include the lines above at the beginning of your scripts 3. make sure your bot scripts use the correct settings, eg. include the lines above at the beginning of your scripts
To display all availible locales on your system run ```locale -a | more```. [Gentoo Wiki](https://wiki.gentoo.org/wiki/UTF-8) To display all available locales on your system run ```locale -a | more```. [Gentoo Wiki](https://wiki.gentoo.org/wiki/UTF-8)
#### Bashbot UTF-8 Support #### Bashbot UTF-8 Support
Bashbot handles all messages transparently, regardless of the charset in use. The only exception is when converting from JSON data to strings. Bashbot handles all messages transparently, regardless of the charset in use. The only exception is when converting from JSON data to strings.
@ -45,19 +45,18 @@ The Emoticons ``` 😁 😘 ❤️ 😊 👍 ``` are encoded as: ``` \uD83D\uDE0
**This "mixed" JSON encoding needs special handling and can not decoded from** ```echo -e``` or ```printf '%s\\n'``` **This "mixed" JSON encoding needs special handling and can not decoded from** ```echo -e``` or ```printf '%s\\n'```
Most complete support for decoding of multibyte characters can only be provided if python is installed on your system. Bbashbot uses an internal, pure bash implementation which is well tested now, even there may some corner cases*.
**Without phyton bashbot falls back to an internal, pure bash implementation which may not work for some corner cases**.
### Run as other user or system service ### Run as other user or system service
Bashbot is desingned to run manually by the user who installed it. Nevertheless it's possible to run it by an other user-ID, as a system service or sceduled from cron. This is onyl recommended for experiend linux users. Bashbot is desingned to run manually by the user who installed it. Nevertheless it's possible to run it by an other user-ID, as a system service or scheduled from cron. This is recommended if you want to bashbot run as a service.
Setup the environment for the user you want to run bashbot and enter desired username, e.g. nobody : Setup the environment for the user you want to run bashbot and enter desired username, e.g. nobody :
```bash ```bash
sudo ./bashbot.sh init sudo ./bashbot.sh init
``` ```
Edit the file ```bashbot.rc``` and edit the following lines to fit your configuration: Edit the file ```bashbot.rc``` and change the following lines to fit your configuration:
```bash ```bash
####################### #######################
# Configuration Section # Configuration Section
@ -82,7 +81,7 @@ sudo ./bashbot.rc start
``` ```
Type ```ps -ef | grep bashbot``` to verify your Bot is running as the desired user. Type ```ps -ef | grep bashbot``` to verify your Bot is running as the desired user.
If your Bot is started by 'bashbot.rc', you must use 'bashbot.rc' also to manage your Bot! The following commands are availible: If your Bot is started by 'bashbot.rc', you must use 'bashbot.rc' also to manage your Bot! The following commands are available:
```bash ```bash
sudo ./bashbot.rc start sudo ./bashbot.rc start
sudo ./bashbot.rc stop sudo ./bashbot.rc stop
@ -93,9 +92,9 @@ sudo ./bashbot.rc killback
``` ```
To change back the environment to your user-ID run ```sudo ./bashbot.sh init``` again and enter your user name. To change back the environment to your user-ID run ```sudo ./bashbot.sh init``` again and enter your user name.
To use bashbot as a system servive include a working ```bashbot.rc``` in your init system (systemd, /etc/init.d). To use bashbot as a system service include a working ```bashbot.rc``` in your init system (systemd, /etc/init.d).
### Scedule bashbot from Cron ### Schedule bashbot from Cron
An example crontab is provided in ```examples/bashbot.cron```. An example crontab is provided in ```examples/bashbot.cron```.
- If you are running bashbot with your user-ID, copy the examples lines to your crontab and remove username ```nobody```. - If you are running bashbot with your user-ID, copy the examples lines to your crontab and remove username ```nobody```.
@ -133,7 +132,7 @@ source /path/to/bashbot.sh source
``` ```
#### Environment variable exported from bashbot #### Environment variable exported from bashbot
If you have sourced 'bashbot.sh' you have the following bashot internal variables availible: If you have sourced 'bashbot.sh' you have the following bashot internal variables available:
```bash ```bash
COMMANDS # default: ./commands.sh" COMMANDS # default: ./commands.sh"
MODULEDIR # default: ./modules" MODULEDIR # default: ./modules"
@ -147,7 +146,7 @@ BOTTOKEN # default: content of ${TOKENFILE}
URL # telegram api URL - default: https://api.telegram.org/bot${BOTTOKEN}" URL # telegram api URL - default: https://api.telegram.org/bot${BOTTOKEN}"
``` ```
#### Interacctive use #### Interactive use
For testing your setup or sending messages yoursel you can use bashbot functions from bash command line: For testing your setup or sending messages yoursel you can use bashbot functions from bash command line:
```bash ```bash
# are we running bash? # are we running bash?
@ -206,7 +205,7 @@ This section describe how you can customize bashbot to your needs by setting env
#### Change file locations #### Change file locations
In standard setup bashbot is self containing, this means you can place 'telegram-bot-bash' any location In standard setup bashbot is self containing, this means you can place 'telegram-bot-bash' any location
and run it from there. All files - programm, config, data etc - will reside in 'telegram-bot-bash'. and run it from there. All files - program, config, data etc - will reside in 'telegram-bot-bash'.
If you want to have other locations for config, data etc, define and export the following environment variables. If you want to have other locations for config, data etc, define and export the following environment variables.
**Note: all specified directories and files must exist or running 'bashbot.sh' will fail.** **Note: all specified directories and files must exist or running 'bashbot.sh' will fail.**
@ -251,7 +250,7 @@ Full path to JSON.sh script, default: './JSON.sh/JSON.sh', must end with '/JSON.
Set bashbot home directory, where bashot will look for additional files. Set bashbot home directory, where bashot will look for additional files.
If BASHBOT_ETC, BASHBOT_VAR or BASHBOT_JSONSH are set the have precedence over BASHBOT_HOME. If BASHBOT_ETC, BASHBOT_VAR or BASHBOT_JSONSH are set the have precedence over BASHBOT_HOME.
This is also usefull if you want to force bashbot to always use full pathnames instead of relative ones. This is also useful if you want to force bashbot to always use full pathnames instead of relative ones.
```bash ```bash
unset BASHBOT_HOME # autodetection (default) unset BASHBOT_HOME # autodetection (default)
export BASHBOT_HOME "" # autodetection export BASHBOT_HOME "" # autodetection
@ -265,7 +264,7 @@ This is also usefull if you want to force bashbot to always use full pathnames i
#### Change config values #### Change config values
##### BASHBOT_URL ##### BASHBOT_URL
Uses given URL instead of offical telegram API URL, useful if you have your own telegram server or for testing. Uses given URL instead of official telegram API URL, useful if you have your own telegram server or for testing.
```bash ```bash
unset BASHBOT_URL # use Telegram URL https://api.telegram.org/bot<token> (default) unset BASHBOT_URL # use Telegram URL https://api.telegram.org/bot<token> (default)
@ -299,8 +298,8 @@ set BASHBOT_CURL to point to it.
``` ```
##### BASHBOT_WGET ##### BASHBOT_WGET
Bashbot uses ```curl``` to communicate with telegram server. if ```curl``` is not availible ```wget``` is used. Bashbot uses ```curl``` to communicate with telegram server. if ```curl``` is not available ```wget``` is used.
If 'BASHBOT_WGET' is set to any value (not undefined or not empty) wget is used even is curl is availible. If 'BASHBOT_WGET' is set to any value (not undefined or not empty) wget is used even is curl is available.
```bash ```bash
unset BASHBOT_WGET # use curl (default) unset BASHBOT_WGET # use curl (default)
export BASHBOT_WGET "" # use curl export BASHBOT_WGET "" # use curl
@ -326,7 +325,7 @@ BASHBOT_TIMEOUT to a numeric value between 1 and 999. Any non numeric or negativ
##### BASHBOT_SLEEP ##### BASHBOT_SLEEP
Instead of polling permanently or with a fixed delay, bashbot offers a simple adaptive polling. Instead of polling permanently or with a fixed delay, bashbot offers a simple adaptive polling.
If messages are received bashbot polls with no dealy. If no messages are availible bashbot add 100ms delay If messages are received bashbot polls with no dealy. If no messages are available bashbot add 100ms delay
for every poll until the maximum of BASHBOT_SLEEP ms. for every poll until the maximum of BASHBOT_SLEEP ms.
```bash ```bash
unset BASHBOT_SLEEP # 5000ms (default) unset BASHBOT_SLEEP # 5000ms (default)
@ -338,7 +337,7 @@ for every poll until the maximum of BASHBOT_SLEEP ms.
``` ```
#### Testet configs as of v0.90 release #### Tested configs as of v0.90 release
**Note: Environment variables are not stored, you must setup them before every call to bashbot.sh, e.g. from a script.** **Note: Environment variables are not stored, you must setup them before every call to bashbot.sh, e.g. from a script.**
##### simple Unix like config, for one bot. bashbot is installed in '/usr/local/telegram-bot-bash' ##### simple Unix like config, for one bot. bashbot is installed in '/usr/local/telegram-bot-bash'
@ -360,7 +359,7 @@ for every poll until the maximum of BASHBOT_SLEEP ms.
/usr/local/bin/bashbot.sh start /usr/local/bin/bashbot.sh start
``` ```
##### simple multibot config, everything is keept inside 'telegram-bot-bash' dir ##### simple multibot config, everything is kept inside 'telegram-bot-bash' dir
```bash ```bash
# config for running Bot 1 # config for running Bot 1
# Note: all dirs and files must exist! # Note: all dirs and files must exist!
@ -382,5 +381,5 @@ for every poll until the maximum of BASHBOT_SLEEP ms.
#### [Prev Advanced Use](3_advanced.md) #### [Prev Advanced Use](3_advanced.md)
#### [Next Best Practice](5_practice.md) #### [Next Best Practice](5_practice.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-pre-0-g03700cd

View File

@ -7,7 +7,7 @@ If you are new to Bot development read [Bots: An introduction for developers](ht
In addition you should know about [BotFather, the one bot to rule them all](https://core.telegram.org/bots#3-how-do-i-create-a-bot). It will help you create new bots and change settings for existing ones. [Commands known by Botfather](https://core.telegram.org/bots#generating-an-authorization-token) In addition you should know about [BotFather, the one bot to rule them all](https://core.telegram.org/bots#3-how-do-i-create-a-bot). It will help you create new bots and change settings for existing ones. [Commands known by Botfather](https://core.telegram.org/bots#generating-an-authorization-token)
If you dont't have a github account, it may time to [sepup a free account now](https://github.com/pricing) If you don't have a github account, it may time to [setup a free account now](https://github.com/pricing)
### Add commands to mycommands.sh only ### Add commands to mycommands.sh only
To ease updates never change ```bashbot.sh```, instead your commands and functions must go to ```mycommands.sh``` . Insert your Bot commands in the ```case ... esac``` block of the 'mycommands()' function: To ease updates never change ```bashbot.sh```, instead your commands and functions must go to ```mycommands.sh``` . Insert your Bot commands in the ```case ... esac``` block of the 'mycommands()' function:
@ -67,17 +67,17 @@ In case you want to add some processing to the global bashbot command add ```ret
``` ```
### Seperate logic from commands ### Separate logic from commands
If a command need more than 2-3 lines of code, you should use a function to seperate logic from command. Place your functions in ```mycommands.sh``` and call the from your command. Example: If a command need more than 2-3 lines of code, you should use a function to separate logic from command. Place your functions in ```mycommands.sh``` and call the from your command. Example:
```bash ```bash
# file: mycommands.sh # file: mycommands.sh
# your additional bahsbot commands # your additional bashbot commands
mycommands() { mycommands() {
case "$MESSAGE" in case "$MESSAGE" in
'/process'*) # logic for /process is done in process_message '/doit'*) # logic for /doit is done in process_message
result="$(process_message "$MESSAGE")" result="$(process_message "$MESSAGE")"
send_normal_message "${CHAT[ID]}" "$result" send_normal_message "${CHAT[ID]}" "$result"
;; ;;
@ -127,7 +127,7 @@ Line 17:
^-- SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'. ^-- SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
``` ```
As you can see my ```mybotcommands.inc.sh``` contains an useless echo command in 'TEXT=' assigment and can be replaced by ```TEXT="${TEXT}${WORD}"``` As you can see my ```mybotcommands.inc.sh``` contains an useless echo command in 'TEXT=' assignment and can be replaced by ```TEXT="${TEXT}${WORD}"```
```bash ```bash
$ shellcheck -x examples/notify $ shellcheck -x examples/notify
OK OK
@ -146,11 +146,11 @@ In bashbot.sh line 490:
CONTACT[USER_ID]="$(sed -n -e '/\["result",'$PROCESS_NUMBER',"message","contact","user_id"\]/ s/.*\][ \t]"\(.*\)"$/\1/p' <"$TMP")" CONTACT[USER_ID]="$(sed -n -e '/\["result",'$PROCESS_NUMBER',"message","contact","user_id"\]/ s/.*\][ \t]"\(.*\)"$/\1/p' <"$TMP")"
^-- SC2034: CONTACT appears unused. Verify it or export it. ^-- SC2034: CONTACT appears unused. Verify it or export it.
``` ```
The example show two warnings in bashbots scripts. The first is a hint you may use shell substitions instead of sed, this is fixed and much faster as the "echo | sed" solution. The example show two warnings in bashbots scripts. The first is a hint you may use shell substitutions instead of sed, this is fixed and much faster as the "echo | sed" solution.
The second warning is about an unused variable, this is true because in our examples CONTACT is not used but assigned in case you want to use it :-) The second warning is about an unused variable, this is true because in our examples CONTACT is not used but assigned in case you want to use it :-)
#### [Prev Best Practice](5_practice.md) #### [Prev Best Practice](5_practice.md)
#### [Next Functions Reference](6_reference.md) #### [Next Functions Reference](6_reference.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -61,7 +61,7 @@ send_normal_message "${CHAT[ID]}" "<b>bold</b> <i>italic><i> <em>italic>/em> <a
``` ```
##### forward_message ##### forward_message
```forward_mesage``` forwards a messsage to the given chat. ```forward_mesage``` forwards a message to the given chat.
*usage:* forward_message "chat_to" "chat_from" "${MESSAGE[ID]}" *usage:* forward_message "chat_to" "chat_from" "${MESSAGE[ID]}"
@ -69,7 +69,7 @@ send_normal_message "${CHAT[ID]}" "<b>bold</b> <i>italic><i> <em>italic>/em> <a
*alias:* _forward "$FROMCHAT" "${MESSAGE[ID]}" *alias:* _forward "$FROMCHAT" "${MESSAGE[ID]}"
See also [Text formating options](https://core.telegram.org/bots/api#formatting-options) See also [Text formatting options](https://core.telegram.org/bots/api#formatting-options)
---- ----
@ -103,8 +103,8 @@ Starting with version 0.80 send_file implements the following rules:
- file names must not contain ".." - file names must not contain ".."
- file names must not start with "." - file names must not start with "."
- file names not starting wit "/" are realtive to $TMPDIR, e.g. ./data-bot-bash - file names not starting with "/" are relative to $TMPDIR, e.g. ./data-bot-bash
- abolute filenames must match $FILE_REGEX - absolute filenames must match $FILE_REGEX
- FILE_REGEX is a regular expression, not shell globbing, test you rexexes: http://www.softlion.com/webTools/RegExpTest/ - FILE_REGEX is a regular expression, not shell globbing, test you rexexes: http://www.softlion.com/webTools/RegExpTest/
*usage:* send_file "${CHAT[ID]}" "file" "caption" *usage:* send_file "${CHAT[ID]}" "file" "caption"
@ -126,7 +126,7 @@ send_file "${CHAT[ID]}" "https://www.domain,com/something.gif" "Something"
---- ----
##### send_keyboard ##### send_keyboard
Note: since version 0.6 send_keyboard was changed to use native "JSON Array" notation as used from Telegram. Example Keybord Array definitions: Note: since version 0.6 send_keyboard was changed to use native "JSON Array" notation as used from Telegram. Example Keyboard Array definitions:
- yes no in two rows: - yes no in two rows:
- OLD format: 'yes' 'no' (two strings) - OLD format: 'yes' 'no' (two strings)
@ -134,7 +134,7 @@ Note: since version 0.6 send_keyboard was changed to use native "JSON Array" not
- new layouts made easy with NEW format: - new layouts made easy with NEW format:
- Yes No in one row: '[ "yes" , "no" ]' - Yes No in one row: '[ "yes" , "no" ]'
- Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]' - Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]'
- numpad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]' - number pad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]'
*usage:* send_keyboard "chat-id" "message" "keyboard" *usage:* send_keyboard "chat-id" "message" "keyboard"
@ -171,11 +171,15 @@ send_button "${CHAT[ID]}" "MAKE MONEY FAST!!!" "Visit my Shop" "https://dealz.rr
``` ```
##### send_inline_keyboard ##### send_inline_keyboard
This allows to place multiple inline buttons in a row. The inline buttons must specified as a JSON array in the following format: Even its called keyboard, this function is different from send_keyboard. The main difference is that it's only possible to
specify URL buttons, no Text Buttons and the Buttons must be an Array of Buttons as specified for
[Telegram InlineMarkup](https://core.telegram.org/bots/api#inlinekeyboardmarkup).
The inline buttons must be specified as a JSON string in the following format:
```[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ]``` ```[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ]```
Each button consists of a pair of text and URL values, sourrounded by '{ }', multiple buttons are seperated by '**,**' and everthing is wrapped in '[ ]'. Each button consists of a pair of text and URL values, sourrounded by '{ }', multiple buttons are separated by '**,**' and everything is wrapped in '[ ]'.
*usage:* send_inline_keyboard "chat-id" "message" "[ {"text":"text", "url":"url"} ...]" *usage:* send_inline_keyboard "chat-id" "message" "[ {"text":"text", "url":"url"} ...]"
@ -204,7 +208,7 @@ If your Bot is a chat admin he can kick and ban a user.
*alias:* _kick_user "${USER[ID]}" *alias:* _kick_user "${USER[ID]}"
##### unban_chat_member ##### unban_chat_member
If your Bot is a chat admine can unban a kicked user. If your Bot is a chat admin can unban a kicked user.
*usage:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}" *usage:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}"
@ -295,7 +299,7 @@ It send back only one response to an inline query.
##### answer_inline_multi ##### answer_inline_multi
anser_inline_multi allows you to send back a list of responses. responses must be seperated by ','. anwser_inline_multi allows you to send back a list of responses. Responses must be separated by ','.
*usage:* answer_inline_multi "${iQUERY[ID]}" "res, res, ... res" *usage:* answer_inline_multi "${iQUERY[ID]}" "res, res, ... res"
@ -333,7 +337,7 @@ Currently the following types and arguments are implemented (optional arguments
"document" title document_URL mime_type (caption description parse_mode) "document" title document_URL mime_type (caption description parse_mode)
"location" latitude longitude title "location" latitude longitude title
"venue" latitude longitude title (adress foursquare) "venue" latitude longitude title (address foursquare)
"contact" phone first (last thumb) "contact" phone first (last thumb)
"cached_photo" file (title description caption parse_mode keyboard) "cached_photo" file (title description caption parse_mode keyboard)
@ -351,7 +355,7 @@ see [InlineQueryResult for more information](https://core.telegram.org/bots/api#
### Background and Interactive jobs ### Background and Interactive jobs
Background functions and interactive jobs extends the bot functionality to not only react to user input. You can start scripts for interative Background functions and interactive jobs extends the bot functionality to not only react to user input. You can start scripts for interactive
chats and send messages based on time or other external events. chats and send messages based on time or other external events.
##### start_proc ##### start_proc
@ -402,7 +406,7 @@ fi
---- ----
##### start_back ##### start_back
Starts a script as a background job and attaches a jobname to it. All output from a background job is sent to the associated chat. 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.
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. 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.
@ -453,7 +457,7 @@ fi
##### send_interactive ##### send_interactive
Form version 0.80 on forward_message is used to forward messages to interactive job. It replaces the old 'inproc' commands used for TMUX. Form version 0.80 on forward_message is used to forward messages to interactive job. It replaces the old 'inproc' commands used for TMUX.
Usually message is automatically forwarded in 'commands.sh', but you can forward messages wihle processing also or send your own messages. Usually a message is automatically forwarded in 'commands.sh', but you can forward messages while processing also or send your own messages.
*usage:* send_interactive "${CHAT[ID]}" "message" *usage:* send_interactive "${CHAT[ID]}" "message"
@ -463,17 +467,17 @@ Usually message is automatically forwarded in 'commands.sh', but you can forwar
### jsshDB ### jsshDB
Since output generated by JSON.sh is so handy to use in bash, we use the format for a simple keys/value file store. Since output generated by JSON.sh is so handy to use in bash, we use the format for a simple keys/value file store.
The file extions is '.jssh' and for security reasons location of jssh files is restricted to BASHBOT_ETC and BASHBOT_DATA.. The file extensions is '.jssh' and for security reasons location of jssh files is restricted to BASHBOT_ETC and BASHBOT_DATA..
#### fast and slow operations #### fast and slow operations
jsshDB files are simple text files and if you append a new Key/value pairs to the end of the file it overwrites jsshDB files are simple text files and if you append a new Key/value pairs to the end of the file it overwrites
an existing key/value pair. We use this behaivor for "fast" file operations. an existing key/value pair. We use this behavior for "fast" file operations.
"fast funtions" add a new key/value pair to the end of the file without deleting an existing one, this is fast but over (long) "fast functions" add a new key/value pair to the end of the file without deleting an existing one, this is fast but over (long)
time the file grows infinitly. time the file grows infinity.
"slow funtions" in contrast modify the key/value pairs in place and write the whole file back, "slow functions" in contrast modify the key/value pairs in place and write the whole file back,
this is slower but clean up the file. All previously added key/value pairs are replaced this is slower but clean up the file. All previously added key/value pairs are replaced
and only the last one is written back to the file. and only the last one is written back to the file.
@ -499,7 +503,7 @@ A jssh fileDB consists of two files which must reside inside BASHBOT_ETC or BAS
Path names containing `..` or not located in BASHBOT_ETC or BASHBOT_DATA are refused by jsshDB functions with an error. Path names containing `..` or not located in BASHBOT_ETC or BASHBOT_DATA are refused by jsshDB functions with an error.
Since version 0.94 jsshDB functions support file locking with flock. write/update operations are serialised with flock to wait until Since version 0.94 jsshDB functions support file locking with flock. Write/update operations are serialised with flock to wait until
previous operations are finished, see "man flock" for information. To avoid deadlocks we use a timeout of 10s for write and 5s for read operations. previous operations are finished, see "man flock" for information. To avoid deadlocks we use a timeout of 10s for write and 5s for read operations.
Every jssh_*DB function exist as jssj_*DB_async also. Every jssh_*DB function exist as jssj_*DB_async also.
@ -515,7 +519,7 @@ In case flock is not availibe or you don't want locking, jssh_*DB_async function
``` ```
##### jssh_newDB ##### jssh_newDB
Creats new empty jsshDB file if not exist. Creates new empty jsshDB file if not exist.
*usage:* jssh_newDB "filename" *usage:* jssh_newDB "filename"
@ -556,7 +560,7 @@ Something wrong with data-bot-bash/../../../somevalues
``` ```
##### jssh_writeDB ##### jssh_writeDB
Write content of an ARRAY into jsshDB file. ARRAY name must be delared with "declare -A ARRAY" before calling writeDB. Write content of an ARRAY into jsshDB file. ARRAY name must be declared with "declare -A ARRAY" before calling writeDB.
"DB" file MUST exist or nothing is written. "DB" file MUST exist or nothing is written.
Note: Existing content is overwritten. Note: Existing content is overwritten.
@ -567,7 +571,7 @@ Note: Existing content is overwritten.
*example:* *example:*
```bash ```bash
# Prepare array to store vaules # Prepare array to store values
declare -A WRITEVALUES declare -A WRITEVALUES
WRITEVALUES["value1"]="example" WRITEVALUES["value1"]="example"
@ -582,7 +586,7 @@ jssh_newDB "${DATADIR:-.}/myvalues"
# write to file data-bot-bash/somevalues.jssh from array MYVALUES # write to file data-bot-bash/somevalues.jssh from array MYVALUES
jssh_writeDB "WRITEVALUES" "${DATADIR:-}/myvalues" jssh_writeDB "WRITEVALUES" "${DATADIR:-}/myvalues"
# show whats written # show what's written
cat "${DATADIR:-}/myvalues.jssh" cat "${DATADIR:-}/myvalues.jssh"
["value1"] "example" ["value1"] "example"
["value2"] "a value" ["value2"] "a value"
@ -592,13 +596,13 @@ cat "${DATADIR:-}/myvalues.jssh"
``` ```
##### jssh_printDB ##### jssh_printDB
Print content of an ARRAY to STDOUT. ARRAY name must be delared with "declare -A ARRAY" before calling printDB.. Print content of an ARRAY to STDOUT. ARRAY name must be declared with "declare -A ARRAY" before calling printDB..
*usage:* jssh_printDB "ARRAY" *usage:* jssh_printDB "ARRAY"
*example:* *example:*
```bash ```bash
# Prepare array to store vaules # Prepare array to store values
declare -A PRINTVALUES declare -A PRINTVALUES
# read file data-bot-bash/myvalues.jssh into array READVALUES # read file data-bot-bash/myvalues.jssh into array READVALUES
@ -614,7 +618,7 @@ jssh_printDB READVALUES
``` ```
##### jssh_updateDB ##### jssh_updateDB
Update/Add content of an ARRAY into a jsshDB file. ARRAY name must be delared with "declare -A ARRAY" before calling updateDB. Update/Add content of an ARRAY into a jsshDB file. ARRAY name must be declared with "declare -A ARRAY" before calling updateDB.
"DB" file MUST exist or nothing is written. "DB" file MUST exist or nothing is written.
Note: Existing content not in ARRAY is kept in file. Note: Existing content not in ARRAY is kept in file.
@ -632,7 +636,7 @@ MYVALUES["newvalue"]="this is new"
# update file data-bot-bash/somevalues.jssh from array MYVALUES # update file data-bot-bash/somevalues.jssh from array MYVALUES
jssh_updateDB "MYVALUES" "${DATADIR:-.}/myvalues" jssh_updateDB "MYVALUES" "${DATADIR:-.}/myvalues"
# show whats written # show what's written
["value1"] "value1" ["value1"] "value1"
["loveit"] "value2" ["loveit"] "value2"
["whynot"] "value3" ["whynot"] "value3"
@ -642,25 +646,25 @@ jssh_updateDB "MYVALUES" "${DATADIR:-.}/myvalues"
cat "$DBfile" cat "$DBfile"
jssh_writeDB "MYVALUES" "${DATADIR:-.}/myvalues" jssh_writeDB "MYVALUES" "${DATADIR:-.}/myvalues"
# show whats written, ups! # show what's written, ups!
cat "$DBfile" cat "$DBfile"
["newvalue"] "this is new" ["newvalue"] "this is new"
``` ```
##### jssh_readDB ##### jssh_readDB
Read content of a file in JSON.sh format into given ARRAY. ARRAY name must be delared with "declare -A ARRAY" upfront, Read content of a file in JSON.sh format into given ARRAY. ARRAY name must be declared with "declare -A ARRAY" upfront,
*usage:* jssh_readDB "ARRAY" "filename" *usage:* jssh_readDB "ARRAY" "filename"
*usage:* jssh_readDB_async "ARRAY" "filename" *usage:* jssh_readDB_async "ARRAY" "filename"
Note: readDB uses concurrent / shared locking from flock so multiple proceses can read from file, as long no process is writing. Note: readDB uses concurrent / shared locking from flock so multiple processes can read from file, as long no process is writing.
Maximum timeour for reading is 1s to not block readers. Maximum timeout for reading is 1s to not block readers.
*example:* *example:*
```bash ```bash
# Prepare array to read vaules # Prepare array to read values
declare -A READVALUES declare -A READVALUES
# read file data-bot-bash/myvalues.jssh into array READVALUES # read file data-bot-bash/myvalues.jssh into array READVALUES
@ -679,7 +683,7 @@ jssh_printDB READVALUES
["whynot","subindex1"] "whynot A" ["whynot","subindex1"] "whynot A"
# access Arrray # access Array
echo "${READVALUES[vaule2]}" echo "${READVALUES[vaule2]}"
a value a value
@ -710,7 +714,7 @@ Insert, update, append a key=value pair to a jsshDB file, key name is only allow
*usage:* jssh_insertKeyDB_asnyc "key" "value" "filename" *usage:* jssh_insertKeyDB_asnyc "key" "value" "filename"
*deprecated:* jssh_insertDB *was renamed in verion 0.96 to* jssh_insertKeyDB *deprecated:* jssh_insertDB *was renamed in version 0.96 to* jssh_insertKeyDB
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. 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.
@ -720,7 +724,7 @@ jssh_insertKeyDB "newkey" "an other value" "${DATADIR:-.}/myvalues"
``` ```
##### jssh_deleteKeyDB ##### jssh_deleteKeyDB
Deleted a key=value pair froma jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._' Deleted a key=value pair from a jsshDB file, key name is only allowed to contain '-a-zA-Z0-9,._'
*usage:* jssh_deleteKeyDB "key" "filename" *usage:* jssh_deleteKeyDB "key" "filename"
@ -735,7 +739,7 @@ jssh_deleteKeyDB "delkey"" "${DATADIR:-.}/myvalues"
Increase a key=value pair from a jsshDB file by 1, key name is only allowed to contain '-a-zA-Z0-9,._' 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. If value is given key is increased by value.
Side effect: if value is given key is updated "in place" (slower) and file is cleand up, if no value is given fast path is used 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
and new count is added to the end of file. and new count is added to the end of file.
*usage:* jssh_countKeyDB "key" "filename" ["value"] *usage:* jssh_countKeyDB "key" "filename" ["value"]
@ -754,7 +758,7 @@ https://linuxconfig.org/how-to-use-arrays-in-bash-script
---- ----
### Aliases - shortcuts for often used funtions ### Aliases - shortcuts for often used functions
Aliases are handy shortcuts for using in 'mycommands.sh', they avoid error prone typing of "${CHAT[ID]}" "${USER[ID]}" as much as possible. Aliases are handy shortcuts for using in 'mycommands.sh', they avoid error prone typing of "${CHAT[ID]}" "${USER[ID]}" as much as possible.
Do not use them in bashbot.sh, modules and addons. Do not use them in bashbot.sh, modules and addons.
@ -854,8 +858,8 @@ Do not use them in bashbot.sh, modules and addons.
### Helper functions ### Helper functions
##### download ##### download
Download the fiven URL ans returns the final filename in TMPDIR. If the given filename exists,the filename is prefixed with a Download the fiven URL and returns the final filename in TMPDIR. If the given filename exists,the filename is prefixed with a
random number. filename is not allowed to contain '/' or '..'. random number. Filename is not allowed to contain '/' or '..'.
*usage:* download URL filename *usage:* download URL filename
@ -876,7 +880,7 @@ Returns true, even if the given function does not exist. Return false if functio
```bash ```bash
_exec_if_function "answer_inline_query" "${iQUERY[ID]}" "Answer params" _exec_if_function "answer_inline_query" "${iQUERY[ID]}" "Answer params"
# fast replacment for module functions exists check: # fast replacement for module functions exists check:
if _is_function "answer_inline_query" if _is_function "answer_inline_query"
then then
"answer_inline_query" "${iQUERY[ID]}" "Answer params" "answer_inline_query" "${iQUERY[ID]}" "Answer params"
@ -919,7 +923,7 @@ Returns PrefixBotname_Postfix
```bash ```bash
# returns botname, if already set # returns botname, if already set
procname procname
# returns unique identifier for everthing related to chat # returns unique identifier for everything related to chat
procname "${CHAT[ID]}" procname "${CHAT[ID]}"
# returns unique identifier for job, regardless of chat # returns unique identifier for job, regardless of chat
procname "" "back-jobname-" procname "" "back-jobname-"
@ -980,13 +984,13 @@ Reads JSON from STDIN and Outputs found String to STDOUT
*usage:* JsonGetString `"path","to","string"` *usage:* JsonGetString `"path","to","string"`
##### JsonGetValue ##### JsonGetValue
Reads JSON fro STDIN and Outputs found Value to STDOUT Reads JSON from STDIN and Outputs found Value to STDOUT
*usage:* JsonGetValue `"path","to","value"` *usage:* JsonGetValue `"path","to","value"`
##### Json2Array ##### Json2Array
Read JSON.sh style data from STDIN and asssign to given ARRAY Read JSON.sh style data from STDIN and assign to given ARRAY
ARRAY name must be declared with "declare -A ARRAY" before calling ARRAY name must be declared with "declare -A ARRAY" before calling
*usage:* Json2Array "ARRAY" *usage:* Json2Array "ARRAY"
@ -1005,13 +1009,13 @@ Output ARRAY as JSON.sh style data to STDOUT
---- ----
##### process_client ##### process_client
Every Message sent to your Bot is processd by this function. It parse the send JSON and assign the found Values to bash variables. Every Message sent to your Bot is processed by this function. It parse the send JSON and assign the found Values to bash variables.
##### process_updates ##### process_updates
If new updates are availible, this functions gets the JSON from Telegram and dispatch it. If new updates are available, this functions gets the JSON from Telegram and dispatch it.
##### process_inline ##### process_inline
Every Inline Message sent to your Bot is processd by this function. It parse the send JSON and assign the found Values to bash variables. 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.
##### start_timer ##### start_timer
Start the the every minute timer ... Start the the every minute timer ...
@ -1028,12 +1032,12 @@ Dispatcher for BASHBOT_EVENT_MESSAGE and related
---- ----
##### getBotName ##### getBotName
The name of your bot is availible as bash variable "$ME", there is no need to call this function if Bot is running. The name of your bot is available as bash variable "$ME", there is no need to call this function if Bot is running.
*usage:* ME="$(getBotName)" *usage:* ME="$(getBotName)"
#### [Prev Best Practice](5_practice.md) #### [Prev Best Practice](5_practice.md)
#### [Next Notes for Developers](7_develop.md) #### [Next Notes for Developers](7_develop.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-72-gfb61a48

View File

@ -11,7 +11,7 @@ If you want to get error messages (and more) start bashbot ```./bashbot.sh star
you can the change the level of verbosity of the debug argument: you can the change the level of verbosity of the debug argument:
``` ```
"debug" all output is redirected to "DEBUG.log", in addtion every incomming message is logged in "MESSAGE.log" and "INLINE.log" "debug" all output is redirected to "DEBUG.log", in addition every incoming message is logged in "MESSAGE.log" and "INLINE.log"
"xdebug" same as debug plus set bash option '-x' to log any executed command in "DEBUG.log" "xdebug" same as debug plus set bash option '-x' to log any executed command in "DEBUG.log"
use the command tail to watch your bot live, e.g. "tail -f DEBUG.log", to obtain more information place set -x; set +x in your code. use the command tail to watch your bot live, e.g. "tail -f DEBUG.log", to obtain more information place set -x; set +x in your code.
@ -28,19 +28,19 @@ you can the change the level of verbosity of the debug argument:
### Modules and Addons ### Modules and Addons
**Modules** resides in ```modules/*.sh``` and are colletions of optional bashbot functions grouped by functionality. Main reason for creating modules was **Modules** resides in ```modules/*.sh``` and are colletions of optional bashbot functions grouped by functionality. Main reason for creating modules was
to keep 'bashbot.sh' small, while extending functionality. In addition not every funtion is needed by all bots, so you can to keep 'bashbot.sh' small, while extending functionality. In addition not every function is needed by all bots, so you can
disable modules, e.g. by rename the respective module file to 'module.sh.off'. disable modules, e.g. by rename the respective module file to 'module.sh.off'.
Modules must use only functions provided by 'bahsbot.sh' or the module itself and sould not depend on other modules or addons. Modules must use only functions provided by 'bahsbot.sh' or the module itself and should not depend on other modules or addons.
The only mandatory module is 'module/sendMessage.sh'. The only mandatory module is 'module/sendMessage.sh'.
If a not mandatory module is used in 'bashbot.sh' or 'commands.sh', the use of ```_is_function``` or If a not mandatory module is used in 'bashbot.sh' or 'commands.sh', the use of ```_is_function``` or
```_execute_if_function``` is mandatory to catch absense of the module. ```_execute_if_function``` is mandatory to catch absence of the module.
**Addons** resides in ```addons/*.sh.dist``` and are not endabled by default. To activate an addon rename it to end with '.sh', e.g. by **Addons** resides in ```addons/*.sh.dist``` and are not endabled by default. To activate an addon rename it to end with '.sh', e.g. by
```cp addons/example.sh.dist addons/example.sh```. ```cp addons/example.sh.dist addons/example.sh```.
Addons must register themself to BASHBOT_EVENTS at startup, e.g. to call a function everytime a message is received. Addons must register themself to BASHBOT_EVENTS at startup, e.g. to call a function every time a message is received.
Addons works similar as 'commands.sh' and 'mycommands.sh' but are much more flexible on when functions/commands are triggered. Addons works similar as 'commands.sh' and 'mycommands.sh' but are much more flexible on when functions/commands are triggered.
Another major difference is: While regular command processing is done in a new sub shell for every command, Another major difference is: While regular command processing is done in a new sub shell for every command,
@ -53,7 +53,7 @@ This prevents blocking or exiting bashbots event loop.
#### Bashbot Events #### Bashbot Events
Addons must register functions to bashbot events by providing their name, and internal identifier and a callback function. Addons must register functions to bashbot events by providing their name, and internal identifier and a callback function.
If an event occours each registered function for the event is called. If an event occurs each registered function for the event is called.
Registered functions run in the same process as bashbot, not as a sub process, so variables set here are persistent as long bashbot is running. Registered functions run in the same process as bashbot, not as a sub process, so variables set here are persistent as long bashbot is running.
@ -61,7 +61,7 @@ Note: For the same reason event function MUST return immediately! Time consuming
##### SEND RECEIVE events ##### SEND RECEIVE events
An RECEIVE event is executed when a Message is received, same iQuery / Message variables are avalible as in commands.sh An RECEIVE event is executed when a Message is received, same iQuery / Message variables are available as in commands.sh
* BASHBOT_EVENT_INLINE an inline query is received * BASHBOT_EVENT_INLINE an inline query is received
@ -89,7 +89,7 @@ BASHBOT_EVENT_TEXT["example_1"]="example_echo"
# function called if a text is received # function called if a text is received
example_echo() { example_echo() {
local event="$1" key="$2" local event="$1" key="$2"
# all availible bashbot functions and variables can be used # all available bashbot functions and variables can be used
send_normal_message "${CHAT[ID]}" "Event: ${event} Key: ${key} : ${MESSAGE[0]}" & # run in background! send_normal_message "${CHAT[ID]}" "Event: ${event} Key: ${key} : ${MESSAGE[0]}" & # run in background!
( MYTEXT="${MESSAGE[0]}" ( MYTEXT="${MESSAGE[0]}"
@ -100,9 +100,9 @@ example_echo() {
An SEND event is executed when a Message is send to telegram. An SEND event is executed when a Message is send to telegram.
* BASHBOT_EVENT_SEND is exceuted if data is send or uploaded to Telegram server * BASHBOT_EVENT_SEND is executed if data is send or uploaded to Telegram server
In contrast to other events, BASHBOT_EVENT_SEND is excecuted in a sub shell, so there is no need to spawn In contrast to other events, BASHBOT_EVENT_SEND is executed in a sub shell, so there is no need to spawn
a background process for longer running commands and changes to variables are not persistent! a background process for longer running commands and changes to variables are not persistent!
BASHBOT_EVENT_SEND is for logging purposes, you must not send messages while processing this event. BASHBOT_EVENT_SEND is for logging purposes, you must not send messages while processing this event.
@ -110,7 +110,7 @@ To avoid wrong use of EVENT_SEND, e.g. fork bomb, event processing is suspended
*usage*: BASHBOT_EVENT_SEND[ "unique-name" ]="callback" *usage*: BASHBOT_EVENT_SEND[ "unique-name" ]="callback"
"callback" is called with paramter "send" or "upload", followed by the arguments used for 'sendJson' or 'upload' functions. "callback" is called with parameter "send" or "upload", followed by the arguments used for 'sendJson' or 'upload' functions.
*Example:* *Example:*
```bash ```bash
@ -128,12 +128,12 @@ example_log(){
##### TIMER events ##### TIMER events
Important: Bashbot timer tick is diabled by default and must be enabled by setting BASHBOT_START_TIMER to any value not zero. Important: Bashbot timer tick is disabled by default and must be enabled by setting BASHBOT_START_TIMER to any value not zero.
* BAHSBOT_EVENT_TIMER executed every minute and can be used in 3 variants: oneshot, once a minute, every X minutes. * BAHSBOT_EVENT_TIMER executed every minute and can be used in 3 variants: oneshot, once a minute, every X minutes.
Registering to BASHBOT_EVENT_TIMER works similar as for message events, but you must add a timing argument to the name. Registering to BASHBOT_EVENT_TIMER works similar as for message events, but you must add a timing argument to the name.
EVENT_TIMER is triggered every 60s and waits until the current running command is finished, so ist not excactly every EVENT_TIMER is triggered every 60s and waits until the current running command is finished, so it's not exactly every
minute, but once a minute. minute, but once a minute.
Every time EVENT_TIMER is triggered the variable "EVENT_TIMER" is increased. each callback is executed if ```EVENT_TIMER % time``` is '0' (true). Every time EVENT_TIMER is triggered the variable "EVENT_TIMER" is increased. each callback is executed if ```EVENT_TIMER % time``` is '0' (true).
@ -144,7 +144,7 @@ This means if you register an every 5 minutes callback first execution may < 5 M
* 0 ignored * 0 ignored
* 1 execute once every minute * 1 execute once every minute
* x execute every x minutes * x execute every x minutes
* -x execute once WHITHIN the next x Minutes (next 10 Minutes since start "event") * -x execute once WITHIN the next x Minutes (next 10 Minutes since start "event")
Note: If you want exact "in x minutes" use "EVENT_TIMER plus x" as time: ```-(EVENT_TIMER + x)``` Note: If you want exact "in x minutes" use "EVENT_TIMER plus x" as time: ```-(EVENT_TIMER + x)```
@ -199,25 +199,25 @@ Now have a look at the directory 'standalone', here you find the files 'bashbot.
A typical bashbot develop loop looks as follow: A typical bashbot develop loop looks as follow:
1. start developing - *change, copy, edit bashbot files ...* 1. start developing - *change, copy, edit bashbot files ...*
2. after changing a bash sript: ```shellcheck -x scipt.sh``` 2. after changing a bash sript: ```shellcheck -x script.sh```
3. ```dev/all-tests.sh``` - *in case if errors back to 2.* 3. ```dev/all-tests.sh``` - *in case if errors back to 2.*
4. ```dev/git-add.sh``` - *check for changed files, update version string, run git add* 4. ```dev/git-add.sh``` - *check for changed files, update version string, run git add*
5. ```git commit -m "COMMIT MESSAGE"; git push``` 5. ```git commit -m "COMMIT MESSAGE"; git push```
**If you setup your dev environment with hooks and use the scripts above, versioning, addding and testing is done automatically.** **If you setup your dev environment with hooks and use the scripts above, versioning, adding and testing is done automatically.**
#### common commands #### common commands
We state bashbot is a bash only bot, but this is not true. bashbot is a bash script using bash features PLUS external commands. We state bashbot is a bash only bot, but this is not true. bashbot is a bash script using bash features PLUS external commands.
Usually bash is used in a unix/linux environment where many (GNU) commands are availible, but if commands are missing, bashbot may not work. Usually bash is used in a unix/linux environment where many (GNU) commands are available, but if commands are missing, bashbot may not work.
To avoid this and make bashbot working on as many platforms as possible - from embedded linux to mainframe - I recommed to restrict To avoid this and make bashbot working on as many platforms as possible - from embedded linux to mainframe - I recommend to restrict
ourself to the common commands provided by bash and coreutils/busybox/toybox. ourself to the common commands provided by bash and coreutils/busybox/toybox.
See [Bash Builtins](https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html), See [Bash Builtins](https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html),
[coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands),
[busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) and [toybox](https://landley.net/toybox/help.html) [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) and [toybox](https://landley.net/toybox/help.html)
Availible commands in bash, coreutils, busybox and toybox. Do you find curl on the list? Available commands in bash, coreutils, busybox and toybox. Do you find curl on the list?
```bash ```bash
.*, [*, [[*, basename, break, builtin*, bzcat, caller*, cat, cd*, chattr, .*, [*, [[*, basename, break, builtin*, bzcat, caller*, cat, cd*, chattr,
chgrp, chmod, chown, clear, command*, continue *, cp, cut, date, declare*, chgrp, chmod, chown, clear, command*, continue *, cp, cut, date, declare*,
@ -230,7 +230,7 @@ Availible commands in bash, coreutils, busybox and toybox. Do you find curl on t
time, times*, timeout, touch, tr, trap*, true, umask*, usleep, uudecode, time, times*, timeout, touch, tr, trap*, true, umask*, usleep, uudecode,
uuencode, wc, wget, which, who, whoami, xargs, yes uuencode, wc, wget, which, who, whoami, xargs, yes
``` ```
commands marked with \* are bash builtins, all others are external programms. Calling an external programm is more expensive then using bulitins commands marked with \* are bash builtins, all others are external programs. Calling an external program is more expensive then using bulitins
or using an internal replacement. Here are some tipps for using builtins.: or using an internal replacement. Here are some tipps for using builtins.:
```bash ```bash
HOST="$(hostname)" -> HOST="$HOSTNAME" HOST="$(hostname)" -> HOST="$HOSTNAME"
@ -243,7 +243,7 @@ data="$(cat file)" -> data="$(<"file")"
DIR="$(dirname $0) -> DIR="${0%/*}" DIR="$(dirname $0) -> DIR="${0%/*}"
IAM="($basename $0)" -> IAM="${0##*/}* PROG="($basename $0)" -> PROG="${0##*/}*
ADDME="$ADDME something to add" -> ADDME+=" something to add"" ADDME="$ADDME something to add" -> ADDME+=" something to add""
@ -283,7 +283,7 @@ For a shell script running as a service it's important to be paranoid about quot
To run shellcheck for a single script run ```shellcheck -x script.sh```, to check all schripts run ```dev/hooks/pre-commit.sh```. To run shellcheck for a single script run ```shellcheck -x script.sh```, to check all schripts run ```dev/hooks/pre-commit.sh```.
### bashbot tests ### bashbot test suite
Starting with version 0.70 bashbot has a test suite. To start testsuite run ```dev/all-tests.sh```. all-tests.sh will return 'SUCCESS' only if all tests pass. Starting with version 0.70 bashbot has a test suite. To start testsuite run ```dev/all-tests.sh```. all-tests.sh will return 'SUCCESS' only if all tests pass.
#### enabling / disabling tests #### enabling / disabling tests
@ -302,7 +302,7 @@ A temporary test environment is created when 'ALL-tests.sh' starts and deleted a
The file ```ALL-tests.inc.sh``` must be included from all tests and provide the test environment as shell variables: The file ```ALL-tests.inc.sh``` must be included from all tests and provide the test environment as shell variables:
```bash ```bash
# Test Evironment # Test Environment
TESTME="$(basename "$0")" TESTME="$(basename "$0")"
DIRME="$(pwd)" DIRME="$(pwd)"
TESTDIR="$1" TESTDIR="$1"
@ -347,5 +347,5 @@ fi
#### [Prev Function Reference](6_reference.md) #### [Prev Function Reference](6_reference.md)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -17,10 +17,10 @@ Two examples for interactive scripts are provided as **calc.sh** and **question.
Background jobs are an easy way to provide sceduled messages or alerts if something happens. Background jobs are an easy way to provide sceduled messages or alerts if something happens.
**notify.sh** is a simple example on how to send a message every x seonds, e.g. current time. **notify.sh** is a simple example on how to send a message every x seonds, e.g. current time.
**background-scripts** contains a more useful example on how to start and stop different scripts plus some example backgound scripts. **background-scripts** contains a more useful example on how to start and stop different scripts plus some example background scripts.
``` ```
mycommands.sh - /run_xxx and /kill-xxx wil start any script named run_xxx.sh mycommands.sh - /run_xxx and /kill-xxx will start any script named run_xxx.sh
run_diskusage.sh - shows disk usage every 100 seconds run_diskusage.sh - shows disk usage every 100 seconds
run_filename.sh - shown the name of new files in a named dir run_filename.sh - shown the name of new files in a named dir
@ -40,7 +40,7 @@ convert existing bots.
mycommands.sh - commands to show system status mycommands.sh - commands to show system status
botacl - controls who can show system status botacl - controls who can show system status
*Availiable commands*: *Available commands*:
/se *sensors* /se *sensors*
/smb *smbstatus* /smb *smbstatus*
/free *memory status* /free *memory status*
@ -55,6 +55,6 @@ convert existing bots.
**external-use** will contain some examples on how to send messages from external scripts to Telegram chats or users. **external-use** will contain some examples on how to send messages from external scripts to Telegram chats or users.
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61

View File

@ -70,7 +70,7 @@ output_telegram() {
sed <<< "${1}" -e ':a;N;$!ba;s/\n/ mynewlinestartshere /g' sed <<< "${1}" -e ':a;N;$!ba;s/\n/ mynewlinestartshere /g'
} # 2>>"$0.log" } # 2>>"$0.log"
# name and localtion of the tml file # name and location of the tml file
# $1 string to output # $1 string to output
# $2 file to add file to # $2 file to add file to

View File

@ -4,11 +4,11 @@
# #
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log

View File

@ -2,11 +2,11 @@
# file: run_filename # file: run_filename
# background job to display content of all new files in WATCHDIR # background job to display content of all new files in WATCHDIR
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log
@ -22,7 +22,7 @@ unset IFS
# discard STDIN for background jobs! # discard STDIN for background jobs!
cat >/dev/null & cat >/dev/null &
# watch for new files created by a trusted programm # watch for new files created by a trusted program
WATCHDIR="/my_trusted/dir_to_watch" WATCHDIR="/my_trusted/dir_to_watch"
source "./mycommands.sh" source "./mycommands.sh"

View File

@ -2,11 +2,11 @@
# file: run_filename # file: run_filename
# background job to display all new files in WATCHDIR # background job to display all new files in WATCHDIR
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log

View File

@ -4,11 +4,11 @@
# #
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log

View File

@ -2,7 +2,7 @@
# file. multibot.sh # file. multibot.sh
# description: run multiple telegram bots from one installation # description: run multiple telegram bots from one installation
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
if [ "${2}" = "" ] || [ "${2}" = "-h" ]; then if [ "${2}" = "" ] || [ "${2}" = "-h" ]; then
echo "Usage: $0 botname command" echo "Usage: $0 botname command"
@ -10,10 +10,10 @@ if [ "${2}" = "" ] || [ "${2}" = "-h" ]; then
fi fi
BOT="${1}" BOT="${1}"
[ "${#BOT}" -lt 5 ] && echo "Botname must have a minumum lenth of 5 characters" && exit 1 [ "${#BOT}" -lt 5 ] && echo "Botname must have a minimum length of 5 characters" && exit 1
# where should the bots live? # where should the bots live?
# true in one dir, false in seperate dirs # true in one dir, false in separate dirs
if true; then if true; then
# example for all in one bashbot dir # example for all in one bashbot dir
BINDIR="/usr/local/telegram-bot-bash" BINDIR="/usr/local/telegram-bot-bash"
@ -21,7 +21,7 @@ if true; then
VAR="${BINDIR}" VAR="${BINDIR}"
else else
# alternative linux like localtions # alternative linux like locations
BINDIR="/usr/local/bin" BINDIR="/usr/local/bin"
ETC="/etc/bahsbot" ETC="/etc/bahsbot"
VAR="/var/bahsbot" VAR="/var/bahsbot"
@ -37,7 +37,7 @@ export BASHBOT_VAR="${VAR}/${BOT}"
[ ! -d "${BINDIR}" ] && echo "Dir ${BINDIR} does not exist" && exit 1 [ ! -d "${BINDIR}" ] && echo "Dir ${BINDIR} does not exist" && exit 1
[ ! -d "${BASHBOT_ETC}" ] && echo "Dir ${BASHBOT_ETC} does not exist" && exit 1 [ ! -d "${BASHBOT_ETC}" ] && echo "Dir ${BASHBOT_ETC} does not exist" && exit 1
[ ! -d "${BASHBOT_VAR}" ] && echo "Dir ${BASHBOT_VAR} does not exist" && exit 1 [ ! -d "${BASHBOT_VAR}" ] && echo "Dir ${BASHBOT_VAR} does not exist" && exit 1
[ ! -x "${BINDIR}/bashbot.sh" ] && echo "${BINDIR}/bashbot.sh not executeable or does not exist" && exit 1 [ ! -x "${BINDIR}/bashbot.sh" ] && echo "${BINDIR}/bashbot.sh not executable or does not exist" && exit 1
[ ! -r "${BASHBOT_ETC}/commands.sh" ] && echo "${BASHBOT_ETC}/commands.sh not readable or does not exist" && exit 1 [ ! -r "${BASHBOT_ETC}/commands.sh" ] && echo "${BASHBOT_ETC}/commands.sh not readable or does not exist" && exit 1
[ ! -r "${BASHBOT_ETC}/mycommands.sh" ] && echo "${BASHBOT_ETC}/mycommands.sh not readable or does not exist" && exit 1 [ ! -r "${BASHBOT_ETC}/mycommands.sh" ] && echo "${BASHBOT_ETC}/mycommands.sh not readable or does not exist" && exit 1

View File

@ -1,5 +1,5 @@
# #
# this is an exmaple crontab file for telegram-bot-bash # this is an example crontab file for telegram-bot-bash
# copy it to /etc/cron.d/bashbot # copy it to /etc/cron.d/bashbot
# #
# (c) https://github.com/gnadelwartz # (c) https://github.com/gnadelwartz
@ -7,7 +7,7 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
SHELL=/bin/sh SHELL=/bin/sh

View File

@ -5,11 +5,11 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log
INPUT="${3:-/dev/stdin}" INPUT="${3:-/dev/stdin}"

View File

@ -4,11 +4,11 @@
# #
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe/log # $3 path to named pipe/log

View File

@ -5,11 +5,11 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe # $3 path to named pipe
INPUT="${3:-/dev/stdin}" INPUT="${3:-/dev/stdin}"

View File

@ -1,9 +1,9 @@
# file: botacl # file: botacl
# a user not listed here, will return false from 'user_is_allowed' # a user not listed here, will return false from 'user_is_allowed'
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# Format: # Format:
# user:ressource:chat # user:resource:chat
# allow user 123456789 access to all resources in all chats # allow user 123456789 access to all resources in all chats
123456789:*:* 123456789:*:*
@ -12,7 +12,7 @@
12131415:systemstatus:* 12131415:systemstatus:*
# * are only allowed on the right hand side and not for user! # * are only allowed on the right hand side and not for user!
# the following exaples are NOT valid! # the following examples are NOT valid!
*:*:* *:*:*
*:start:* *:start:*
*:*:98979695 *:*:98979695

View File

@ -5,7 +5,7 @@
# to show how you can customize bashbot by only editing mycommands.sh # to show how you can customize bashbot by only editing mycommands.sh
# NOTE: this is not tested, simply copied from original source and reworked! # NOTE: this is not tested, simply copied from original source and reworked!
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# #
# shellcheck disable=SC2154 # shellcheck disable=SC2154
# shellcheck disable=SC2034 # shellcheck disable=SC2034
@ -19,7 +19,7 @@
bashbot_info='This bot allows you to request status of your system. bashbot_info='This bot allows you to request status of your system.
To begin using the bot, try with the /help command. To begin using the bot, try with the /help command.
' '
bashbot_help='*Availiable commands*: bashbot_help='*Available commands*:
/se *sensors* /se *sensors*
/smb *smbstatus* /smb *smbstatus*
/free *memory status* /free *memory status*

View File

@ -5,9 +5,9 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-17-gdda5b6d
# #
# source from commands.sh to use the aliases # will be automatically sourced from bashbot
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"

View File

@ -5,9 +5,9 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# source from commands.sh to use the inline functions # will be automatically sourced from bashbot
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
@ -32,7 +32,7 @@ inline_query_compose(){
# title2Json title caption description markup inlinekeyboard # title2Json title caption description markup inlinekeyboard
case "${2}" in case "${2}" in
# user provided media # user provided media
"article"|"message") # article ID title message (markup decription) "article"|"message") # article ID title message (markup description)
JSON='{"type":"article","id":"'$ID'","input_message_content": {"message_text":"'$4'"} '$(title2Json "$3" "" "$5" "$6" "$7")'}' JSON='{"type":"article","id":"'$ID'","input_message_content": {"message_text":"'$4'"} '$(title2Json "$3" "" "$5" "$6" "$7")'}'
;; ;;
"photo") # photo ID photoURL (thumbURL title description caption) "photo") # photo ID photoURL (thumbURL title description caption)
@ -62,7 +62,7 @@ inline_query_compose(){
"location") # location ID lat long title "location") # location ID lat long title
JSON='{"type":"location","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}' JSON='{"type":"location","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}'
;; ;;
"venue") # venue ID lat long title (adress forsquare) "venue") # venue ID lat long title (address forsquare)
[ -z "$6" ] && addr="$5" [ -z "$6" ] && addr="$5"
[ -n "$7" ] && fours=',"foursquare_id":"'$7'"' [ -n "$7" ] && fours=',"foursquare_id":"'$7'"'
JSON='{"type":"venue","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'","address":"'$6${addr}'"'${fours}'}' JSON='{"type":"venue","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'","address":"'$6${addr}'"'${fours}'}'

View File

@ -5,9 +5,9 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# source from commands.sh if you want ro use interactive or background jobs # will be automatically sourced from bashbot
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
@ -35,20 +35,22 @@ killproc() {
kill_proc "${CHAT[ID]}" "$1" kill_proc "${CHAT[ID]}" "$1"
} }
# inline and backgound functions # inline and background functions
# $1 chatid # $1 chatid
# $2 program # $2 program
# $3 jobname # $3 jobname
# $4 $5 parameters # $4 $5 parameters
start_back() { start_back() {
#local fifo; fifo="${DATADIR:-.}/$(procname "$1")" local cmdfile; cmdfile="${DATADIR:-.}/$(procname "$1")$3-back.cmd"
printf '%s\n' "$1:$3:$2" >"${DATADIR:-.}/$(procname "$1")$3-back.cmd" printf '%s\n' "$1:$3:$2" >"${cmdfile}"
restart_back "$@" restart_back "$@"
} }
restart_back() { restart_back() {
local fifo; fifo="${DATADIR:-.}/$(procname "$1" "back-$3-")" local fifo; fifo="${DATADIR:-.}/$(procname "$1" "back-$3-")"
kill_proc "$1" "back-$3-" printf "%s: Start background job CHAT=%s JOB=%s CMD=%s\n" "$(date)" "${1}" "${fifo##*/}" "${2} ${4} ${5}" >>"${UPDATELOG}"
check_back "$1" "$3" && kill_proc "$1" "back-$3-"
nohup bash -c "{ $2 \"$4\" \"$5\" \"${fifo}\" | \"${SCRIPT}\" outproc \"${1}\" \"${fifo}\"; }" &>>"${fifo}.log" & nohup bash -c "{ $2 \"$4\" \"$5\" \"${fifo}\" | \"${SCRIPT}\" outproc \"${1}\" \"${fifo}\"; }" &>>"${fifo}.log" &
sleep 0.5 # give bg job some time to init
} }
@ -59,7 +61,8 @@ start_proc() {
[ -z "$2" ] && return [ -z "$2" ] && return
[ -x "${2%% *}" ] || return 1 [ -x "${2%% *}" ] || return 1
local fifo; fifo="${DATADIR:-.}/$(procname "$1")" local fifo; fifo="${DATADIR:-.}/$(procname "$1")"
kill_proc "$1" printf "%s: Start interacitve script CHAT=%s JOB=%s CMD=%s\n" "$(date)" "${1}" "${fifo##*/}" "${2} ${3} ${4}" >>"${UPDATELOG}"
check_proc "$1" && kill_proc "$1"
mkfifo "${fifo}" mkfifo "${fifo}"
nohup bash -c "{ $2 \"$4\" \"$5\" \"$fifo\" | \"${SCRIPT}\" outproc \"${1}\" \"${fifo}\" nohup bash -c "{ $2 \"$4\" \"$5\" \"$fifo\" | \"${SCRIPT}\" outproc \"${1}\" \"${fifo}\"
rm \"${fifo}\"; [ -s \"${fifo}.log\" ] || rm -f \"${fifo}.log\"; }" &>>"${fifo}.log" & rm \"${fifo}\"; [ -s \"${fifo}.log\" ] || rm -f \"${fifo}.log\"; }" &>>"${fifo}.log" &
@ -95,6 +98,7 @@ kill_proc() {
fifo="$(procname "$1" "$2")" fifo="$(procname "$1" "$2")"
prid="$(proclist "${fifo}")" prid="$(proclist "${fifo}")"
fifo="${DATADIR:-.}/${fifo}" fifo="${DATADIR:-.}/${fifo}"
printf "%s: Stop interacitve / background CHAT=%s JOB=%s\n" "$(date)" "${1}" "${fifo##*/}" >>"${UPDATELOG}"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
[ -n "${prid}" ] && kill ${prid} [ -n "${prid}" ] && kill ${prid}
[ -s "${fifo}.log" ] || rm -f "${fifo}.log" [ -s "${fifo}.log" ] || rm -f "${fifo}.log"
@ -113,13 +117,15 @@ inproc() {
send_interactive "${CHAT[ID]}" "${MESSAGE}" send_interactive "${CHAT[ID]}" "${MESSAGE}"
} }
# start stopp all jobs # start stop all jobs
# $1 command # $1 command
# killb* # killb*
# suspendb* # suspendb*
# resumeb* # resumeb*
job_control() { job_control() {
local content proc CHAT job fifo killall="" local BOT ADM content proc CHAT job fifo killall=""
BOT="$(getConfigKey "botname")"
ADM="$(getConfigKey "botadmin")"
for FILE in "${DATADIR:-.}/"*-back.cmd; do for FILE in "${DATADIR:-.}/"*-back.cmd; do
[ "${FILE}" = "${DATADIR:-.}/*-back.cmd" ] && echo -e "${RED}No background processes.${NC}" && break [ "${FILE}" = "${DATADIR:-.}/*-back.cmd" ] && echo -e "${RED}No background processes.${NC}" && break
content="$(< "${FILE}")" content="$(< "${FILE}")"
@ -132,19 +138,27 @@ job_control() {
"resumeb"*|"backgr"*) "resumeb"*|"backgr"*)
printf "Restart Job: %s %s\n" "${proc}" " ${fifo}" printf "Restart Job: %s %s\n" "${proc}" " ${fifo}"
restart_back "${CHAT}" "${proc}" "${job}" restart_back "${CHAT}" "${proc}" "${job}"
# inform botadmin about stop
[ -n "${ADM}" ] && send_normal_message "${ADM}" "Bot ${BOT} restart background jobs ..." &
;; ;;
"suspendb"*) "suspendb"*)
printf "Suspend Job: %s %s\n" "${proc}" " ${fifo}" printf "Suspend Job: %s %s\n" "${proc}" " ${fifo}"
kill_proc "${CHAT}" "${job}" kill_proc "${CHAT}" "${job}"
# inform botadmin about stop
[ -n "${ADM}" ] && send_normal_message "${ADM}" "Bot ${BOT} suspend background jobs ..." &
killall="y" killall="y"
;; ;;
"killb"*) "killb"*)
printf "Kill Job: %s %s\n" "${proc}" " ${fifo}" printf "Kill Job: %s %s\n" "${proc}" " ${fifo}"
kill_proc "${CHAT}" "${job}" kill_proc "${CHAT}" "${job}"
rm -f "${FILE}" # remove job rm -f "${FILE}" # remove job
# inform botadmin about stop
[ -n "${ADM}" ] && send_normal_message "${ADM}" "Bot ${BOT} kill background jobs ..." &
killall="y" killall="y"
;; ;;
esac esac
# send message only onnfirst job
ADM=""
done done
# kill all requestet. kill ALL background jobs, even not listed in data-bot-bash # kill all requestet. kill ALL background jobs, even not listed in data-bot-bash
[ "${killall}" = "y" ] && killallproc "back-" [ "${killall}" = "y" ] && killallproc "back-"

View File

@ -5,13 +5,13 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-17-gdda5b6d
# will be automatically sourced from bashbot
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# source from commands.sh to use the member functions
LEAVE_URL=$URL'/leaveChat' LEAVE_URL=$URL'/leaveChat'
KICK_URL=$URL'/kickChatMember' KICK_URL=$URL'/kickChatMember'
UNBAN_URL=$URL'/unbanChatMember' UNBAN_URL=$URL'/unbanChatMember'
@ -50,11 +50,11 @@ user_is_admin() {
} }
user_is_botadmin() { user_is_botadmin() {
local admin; admin="$(head -n 1 "${BOTADMIN}")" local admin; admin="$(getConfigKey "botadmin")"
[ "${admin}" = "${1}" ] && return 0 [ "${admin}" = "${1}" ] && return 0
[ "${admin}" = "${2}" ] && return 0 [ "${admin}" = "${2}" ] && return 0
[[ "${admin}" = "@*" ]] && [[ "${admin}" = "${2}" ]] && return 0 [[ "${admin}" = "@*" ]] && [[ "${admin}" = "${2}" ]] && return 0
if [ "${admin}" = "?" ]; then printf '%s\n' "${1:-?}" >"${BOTADMIN}"; return 0; fi if [ "${admin}" = "?" ]; then setConfigKey "${1:-?}"; return 0; fi
return 1 return 1
} }

View File

@ -5,164 +5,138 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-1-g15e6c7b #### $$VERSION$$ v0.98-dev-74-g3d760b3
# #
# source from commands.sh to use jsonDB functions # source from commands.sh to use jsonDB functions
# #
# jsonDB provides simple functions to read and store bash Arrays # jsonDB provides simple functions to read and store bash Arrays
# from to file in JSON.sh output format, its a simple key/value storage. # from to file in JSON.sh output format, its a simple key/value storage.
# will be automatically sourced from bashbot
# but can be used independent from bashbot also
# e.g. to create scrupts to manage jssh files
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
# new feature: serialize / atomic operations: # new feature: serialize / atomic operations:
# updates will be done atomic with flock # updates will be done atomic with flock
# flock should flock should be availible on all system as its part of busybox # flock should flock should be available on all system as its part of busybox
# tinybox # tinybox
# lockfile filename.flock is persistent and will be testet with flock for active lock (file open) # lockfile filename.flock is persistent and will be testet with flock for active lock (file open)
export BASHBOT_LOCKNAME=".flock" export JSSH_LOCKNAME=".flock"
# use flock if command exist
if [ "$(LC_ALL=C type -t "flock")" = "file" ]; then
if _exists flock; then
############### ###############
# we have flock # we have flock
# use flock for atomic operations # use flock for atomic operations
# read content of a file in JSON.sh format into given ARRAY # read content of a file in JSON.sh format into given ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront # $1 ARRAY name, must be declared with "declare -A ARRAY" upfront
# $2 filename, must be relative to BASHBOT_ETC, and not contain '..' # $2 filename, must be relative to BASHBOT_ETC, and not contain '..'
jssh_readDB() { jssh_readDB() {
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2 [ ! -f "${DB}" ] && return 2
# shared lock, many processes can read, max wait 1s # shared lock, many processes can read, max wait 1s
{ flock -s -w 1 200; Json2Array "$1" <"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}" { flock -s -w 1 200; Json2Array "$1" <"${DB}"; } 200>"${DB}${JSSH_LOCKNAME}"
} }
# write ARRAY content to a file in JSON.sh format # write ARRAY content to a file in JSON.sh format
# Warning: old content is overwritten # Warning: old content is overwritten
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront # $1 ARRAY name, must be declared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_writeDB() { jssh_writeDB() {
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2 [ ! -f "${DB}" ] && return 2
# exclusive lock, no other process can read or write, maximum wait to get lock is 10s # exclusive lock, no other process can read or write, maximum wait to get lock is 10s
{ flock -e -w 10 200; Array2Json "$1" >"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}" { flock -e -w 10 200; Array2Json "$1" >"${DB}"; } 200>"${DB}${JSSH_LOCKNAME}"
} }
# update/write ARRAY content in file without deleting keys not in ARRAY # update/write ARRAY content in file without deleting keys not in ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront # $1 ARRAY name, must be declared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
# complex slow, warpper async
jssh_updateDB() { jssh_updateDB() {
# for atomic update we cant use read/writeDB # for atomic update we can't use read/writeDB
local DB; DB="$(jssh_checkDB "$2")" [ -z "${2}" ] && return 1
[ -z "${DB}" ] && return 1 local DB="${2}.jssh" # check in async
[ ! -f "${DB}" ] && return 2 [ ! -f "${DB}" ] && return 2
declare -n ARRAY="$1" { flock -e -w 10 200; jssh_updateDB_async "$@"; } 200>"${DB}${JSSH_LOCKNAME}"
[ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR
# start atomic update here, exclusive max wait 10s
{ flock -e -w 10 200
Json2Array "oldARR" <"${DB}"
if [ -z "${oldARR[*]}" ]; then
# no old content
Array2Json "$1" >"${DB}"
else
# merge arrays
local key
for key in "${!ARRAY[@]}"
do
oldARR["${key}"]="${ARRAY["${key}"]}"
done
Array2Json "oldARR" >"${DB}"
fi
} 200>"${DB}${BASHBOT_LOCKNAME}"
} }
# insert, update, apped key/value to jsshDB # insert, update, apped key/value to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 key value # $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
alias jssh_insertDB=jssh_insertKeyDB # backward compatibility alias jssh_insertDB=jssh_insertKeyDB # backward compatibility
# renamed to be more consistent # renamed to be more consistent
jssh_insertKeyDB() { jssh_insertKeyDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")" local DB; DB="$(jssh_checkDB "$3")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2 [ ! -f "${DB}" ] && return 2
# start atomic update here, exclusive max wait 2, it's append, not overwrite # start atomic update here, exclusive max wait 2, it's append, not overwrite
{ flock -e -w 2 200 { flock -e -w 2 200
# it's append, but last one counts, its a simple DB ... # it's append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}" printf '["%s"]\t"%s"\n' "${1//,/\",\"}" "${2//\"/\\\"}" >>"${DB}"
} 200>"${DB}${BASHBOT_LOCKNAME}" } 200>"${DB}${JSSH_LOCKNAME}"
} }
# delete key/value from jsshDB # delete key/value from jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
# medium complex slow, wrapper async
jssh_deleteKeyDB() { jssh_deleteKeyDB() {
[ -z "${2}" ] && return 1
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB; DB="$(jssh_checkDB "$2")" local DB="${2}.jssh"
declare -A oldARR
# start atomic delete here, exclusive max wait 10s # start atomic delete here, exclusive max wait 10s
{ flock -e -w 10 200 { flock -e -w 10 200; jssh_deleteKeyDB_async "$@"; } 200>"${DB}${JSSH_LOCKNAME}"
Json2Array "oldARR" <"${DB}"
unset oldARR["$1"]
Array2Json "oldARR" >"${DB}"
} 200>"${DB}${BASHBOT_LOCKNAME}"
} }
# get key/value from jsshDB # get key/value from jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
alias jssh_getDB=jssh_getKeyDB alias jssh_getDB=jssh_getKeyDB
jssh_getKeyDB() { jssh_getKeyDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
declare -A oldARR [ -z "${DB}" ] && return 1
# start atomic delete here, exclusive max wait 1s # start atomic delete here, exclusive max wait 1s
{ flock -s -w 1 200 { flock -s -w 1 200
Json2Array "oldARR" <"${DB}" [ -r "${DB}" ] && sed -n 's/\["'"$1"'"\]\t*"\(.*\)"/\1/p' <"${DB}" | tail -n 1
} 200>"${DB}${BASHBOT_LOCKNAME}" } 200>"${DB}${JSSH_LOCKNAME}"
printf '%s' "${oldARR["$1"]}"
} }
# add a value to key, used for conters # add a value to key, used for conters
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
# $3 optional count, value added to counter, add 1 if empty # $3 optional count, value added to counter, add 1 if empty
# side effect: if $3 is not given, we add to end of file to be as fast as possible # side effect: if $3 is not given, we add to end of file to be as fast as possible
# complex, wrapper to async
jssh_countKeyDB() { jssh_countKeyDB() {
[ -z "${2}" ] && return 1
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB; DB="$(jssh_checkDB "$2")" local DB="${2}.jssh"
declare -A oldARR
# start atomic delete here, exclusive max wait 5 # start atomic delete here, exclusive max wait 5
{ flock -e -w 5 200 { flock -e -w 5 200; jssh_countKeyDB_async "$@"; } 200>"${DB}${JSSH_LOCKNAME}"
Json2Array "oldARR" <"${DB}"
if [ "$3" != "" ]; then
(( oldARR["$1"]+="$3" ));
Array2Json "oldARR" >"${DB}"
else
# it's append, but last one counts, its a simple DB ...
(( oldARR["$1"]++ ));
printf '["%s"]\t"%s"\n' "${1//,/\",\"}" "${oldARR["$1"]//\"/\\\"}" >>"${DB}"
fi
} 200>"${DB}${BASHBOT_LOCKNAME}"
} }
# update key/value in place to jsshDB # update key/value in place to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 key value # $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
#no own locking, so async is the same as updatekeyDB #no own locking, so async is the same as updatekeyDB
jssh_updateKeyDB() { jssh_updateKeyDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
[ -z "${3}" ] && return 1
declare -A updARR declare -A updARR
# shellcheck disable=SC2034 # shellcheck disable=SC2034
updARR["$1"]="$2" updARR["$1"]="$2"
@ -173,14 +147,25 @@ if _exists flock; then
jssh_clearDB() { jssh_clearDB() {
local DB; DB="$(jssh_checkDB "$1")" local DB; DB="$(jssh_checkDB "$1")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
{ flock -e -w 10 200 { flock -e -w 10 200; printf '' >"${DB}"; } 200>"${DB}${JSSH_LOCKNAME}"
printf '' >"${DB}"
} 200>"${DB}${BASHBOT_LOCKNAME}"
} }
# updates Array if DB file has changed since last call
# $1 name of array to update
# $2 database
# $3 id used to identify caller
# medium complex, wrapper async
jssh_updateArray() {
[ -z "${2}" ] && return 1
local DB="${2}.jssh" # name check in async
[ ! -f "${DB}" ] && return 2
declare -n ARRAY="$1"
[[ -z "${ARRAY[*]}" || "${DB}" -nt "${DB}.last${3}" ]] && touch "${DB}.last${3}" && jssh_readDB "${1}" "${2}"
}
else else
######### #########
# we have no flock, use "old" not atomic functions # we have no flock, use non atomic functions
alias jssh_readDB=ssh_readDB_async alias jssh_readDB=ssh_readDB_async
alias jssh_writeDB=jssh_writeDB_async alias jssh_writeDB=jssh_writeDB_async
alias jssh_updateDB=jssh_updateDB_async alias jssh_updateDB=jssh_updateDB_async
@ -191,13 +176,14 @@ else
alias jssh_countKeyDB=jssh_countKeyDB_async alias jssh_countKeyDB=jssh_countKeyDB_async
alias jssh_updateKeyDB=jssh_updateKeyDB_async alias jssh_updateKeyDB=jssh_updateKeyDB_async
alias jssh_clearDB=jssh_clearDB_async alias jssh_clearDB=jssh_clearDB_async
alias jssh_updateArray=updateArray_async
fi fi
############## ##############
# no need for atomic # no need for atomic
# print ARRAY content to stdout instead of file # print ARRAY content to stdout instead of file
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront # $1 ARRAY name, must be declared with "declare -A ARRAY" upfront
jssh_printDB_async() { jssh_printDB "$@"; } jssh_printDB_async() { jssh_printDB "$@"; }
jssh_printDB() { jssh_printDB() {
Array2Json "$1" Array2Json "$1"
@ -214,23 +200,23 @@ jssh_newDB() {
# $1 filename, check filename, it must be relative to BASHBOT_VAR, and not contain '..' # $1 filename, check filename, it must be relative to BASHBOT_VAR, and not contain '..'
# returns real path to DB file if everything is ok # returns real path to DB file if everything is ok
jssh_checkDB_sync() { jssh_checkDB "$@"; } jssh_checkDB_async() { jssh_checkDB "$@"; }
jssh_checkDB(){ jssh_checkDB(){
local DB local DB
[ -z "$1" ] && return 1 [ -z "$1" ] && return 1
[[ "$1" = *'..'* ]] && return 2 [[ "$1" = *'..'* ]] && return 2
if [[ "$1" == "${BASHBOT_VAR:-.}"* ]] || [[ "$1" == "${BASHBOT_DATA:-.}"* ]]; then if [[ "$1" == "${BASHBOT_VAR:-.}"* ]] || [[ "$1" == "${BASHBOT_DATA:-.}"* ]]; then
DB="$1.jssh" DB="${1}.jssh"
else else
DB="${BASHBOT_VAR:-.}/$1.jssh" DB="${BASHBOT_VAR:-.}/${1}.jssh"
fi fi
printf '%s' "${DB}" [ "${DB}" != ".jssh" ] && printf '%s' "${DB}"
} }
###################### ######################
# implementations as non atomic functions # implementations as non atomic functions
# can be used explictitly or as fallback if flock is not availible # can be used explictitly or as fallback if flock is not available
jssh_readDB_async() { jssh_readDB_async() {
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
@ -246,6 +232,7 @@ jssh_writeDB_async() {
} }
jssh_updateDB_async() { jssh_updateDB_async() {
[ -z "${2}" ] && return 1
declare -n ARRAY="$1" declare -n ARRAY="$1"
[ -z "${ARRAY[*]}" ] && return 1 [ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR declare -A oldARR
@ -267,18 +254,18 @@ jssh_updateDB_async() {
jssh_insertDB_async() { jssh_insertKeyDB "$@"; } jssh_insertDB_async() { jssh_insertKeyDB "$@"; }
jssh_insertKeyDB_async() { jssh_insertKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")" local DB; DB="$(jssh_checkDB "$3")"
[ -z "${DB}" ] && return 1 [ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2 [ ! -f "${DB}" ] && return 2
# its append, but last one counts, its a simple DB ... # its append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}" printf '["%s"]\t"%s"\n' "${1//,/\",\"}" "${2//\"/\\\"}" >>"${DB}"
} }
jssh_deleteKeyDB_async() { jssh_deleteKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
declare -A oldARR declare -A oldARR
Json2Array "oldARR" <"${DB}" Json2Array "oldARR" <"${DB}"
unset oldARR["$1"] unset oldARR["$1"]
@ -288,29 +275,35 @@ jssh_deleteKeyDB_async() {
jssh_getKeyDB_async() { jssh_getKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB; DB="$(jssh_checkDB "$2")" local DB; DB="$(jssh_checkDB "$2")"
declare -A oldARR [ -z "${DB}" ] && return 1
Json2Array "oldARR" <"${DB}" [ -r "${DB}" ] && sed -n 's/\["'"$1"'"\]\t*"\(.*\)"/\1/p' <"${DB}" | tail -n 1
printf '%s' "${oldARR["$1"]}"
} }
jssh_countKeyDB_async() { jssh_countKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local DB COUNT="1"; DB="$(jssh_checkDB "$2")" local VAL DB; DB="$(jssh_checkDB "$2")"
[ "$3" != "" ] && COUNT="$3" [ -z "${DB}" ] && return 1
declare -A oldARR # start atomic delete here, exclusive max wait 5
# start atomic delete here, exclusive max wait 10s if [ -n "$3" ]; then
Json2Array "oldARR" <"${DB}" declare -A oldARR
(( oldARR["$1"]+=COUNT )); Json2Array "oldARR" <"${DB}"
Array2Json "oldARR" >"${DB}" (( oldARR["$1"]+="$3" ));
} Array2Json "oldARR" >"${DB}"
elif [ -r "${DB}" ]; then
# it's append, but last one counts, its a simple DB ...
VAL="$(sed -n 's/\["'"$1"'"\]\t*"\(.*\)"/\1/p' <"${DB}" | tail -n 1)"
printf '["%s"]\t"%s"\n' "${1//,/\",\"}" "$((++VAL))" >>"${DB}"
fi
}
# updatie key/value in place to jsshDB # updatie key/value in place to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._ # $1 key name, can only contain -a-zA-Z0-9,._
# $2 key value # $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' # $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
#no own locking, so async is the same as updatekeyDB #no own locking, so async is the same as updatekeyDB
jssh_updateKeyDB_async() { jssh_updateKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3 [[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
[ -z "${3}" ] && return 1
declare -A updARR declare -A updARR
# shellcheck disable=SC2034 # shellcheck disable=SC2034
updARR["$1"]="$2" updARR["$1"]="$2"
@ -323,4 +316,36 @@ jssh_clearDB_async() {
printf '' >"${DB}" printf '' >"${DB}"
} }
function jssh_updateArray_async() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
declare -n ARRAY="$1"
[[ -z "${ARRAY[*]}" || "${DB}" -nt "${DB}.last${3}" ]] && touch "${DB}.last${3}" && jssh_readDB_async "${1}" "${2}"
}
##############
# these 2 functions does all key/value store "magic"
# and convert from/to bash array
# read JSON.sh style data and asssign to an ARRAY
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling
Json2Array() {
# shellcheck disable=SC1091,SC1090
[ -z "$1" ] || source <( printf "$1"'=( %s )' "$(sed -E -n -e '/\["[-0-9a-zA-Z_,."]+"\]\+*\t/ s/\t/=/gp' -e 's/=(true|false)/="\1"/')" )
}
# get Config Key from jssh file without jsshDB
# output ARRAY as JSON.sh style data
# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling
Array2Json() {
[ -z "$1" ] && return 1
local key val
declare -n ARRAY="$1"
for key in "${!ARRAY[@]}"
do
# in case val contains newline convert to \n
val="${ARRAY[${key}]//$'\n'/\\n}"
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${val//\"/\\\"}"
done
}

View File

@ -5,7 +5,9 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# will be automatically sourced from bashbot
# source once magic, function named like file # source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }" eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
@ -25,10 +27,19 @@ ACTION_URL=$URL'/sendChatAction'
FORWARD_URL=$URL'/forwardMessage' FORWARD_URL=$URL'/forwardMessage'
send_normal_message() { send_normal_message() {
local text; text="$(JsonEscape "${2}")" local len text; text="$(JsonEscape "${2}")"
text="${text//$'\n'/\\n}"
until [ -z "${text}" ]; do until [ -z "${text}" ]; do
sendJson "${1}" '"text":"'"${text:0:4096}"'"' "${MSG_URL}" if [ "${#text}" -le 4096 ]; then
text="${text:4096}" sendJson "${1}" '"text":"'"${text}"'"' "${MSG_URL}"
break
else
len=4095
[ "${text:4095:2}" != "\n" ] &&\
len="${text:0:4096}" && len="${len%\\n*}" && len="${#len}"
sendJson "${1}" '"text":"'"${text:0:${len}}"'"' "${MSG_URL}"
text="${text:$((len+2))}"
fi
done done
} }
@ -73,13 +84,13 @@ old_send_keyboard() {
ISEMPTY="ThisTextIsEmptyAndWillBeDeleted" ISEMPTY="ThisTextIsEmptyAndWillBeDeleted"
sendEmpty() { sendEmpty() {
sendJson "${@}" sendJson "${@}"
[[ "${2}" = *"${ISEMPTY}"* ]] && delete_message "${1}" "${BOTSENT[ID]}" [[ "${2}" = *"${ISEMPTY}"* ]] && delete_message "${1}" "${BOTSENT[ID]}" "nolog"
} }
send_keyboard() { send_keyboard() {
if [[ "$3" != *'['* ]]; then old_send_keyboard "${@}"; return; fi if [[ "$3" != *'['* ]]; then old_send_keyboard "${@}"; return; fi
local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"${ISEMPTY}"'"' local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"Keyboard:"'"'
local one_time=', "one_time_keyboard":true' && [ -n "$4" ] && one_time="" local one_time=', "one_time_keyboard":true' && [ -n "$4" ] && one_time=""
sendEmpty "${1}" "${text}"', "reply_markup": {"keyboard": [ '"${3}"' ] '"${one_time}"'}' "$MSG_URL" sendJson "${1}" "${text}"', "reply_markup": {"keyboard": [ '"${3}"' ] '"${one_time}"'}' "$MSG_URL"
# '"text":"$2", "reply_markup": {"keyboard": [ ${3} ], "one_time_keyboard": true}' # '"text":"$2", "reply_markup": {"keyboard": [ ${3} ], "one_time_keyboard": true}'
} }
@ -89,8 +100,8 @@ remove_keyboard() {
#JSON='"text":"$2", "reply_markup": {"remove_keyboard":true}' #JSON='"text":"$2", "reply_markup": {"remove_keyboard":true}'
} }
send_inline_keyboard() { send_inline_keyboard() {
local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"${ISEMPTY}"'"' local text; text='"text":"'$(JsonEscape "${2}")'"'; [ -z "${2}" ] && text='"text":"'"Keyboard:"'"'
sendEmpty "${1}" "${text}"', "reply_markup": {"inline_keyboard": [ '"${3}"' ]}' "$MSG_URL" sendJson "${1}" "${text}"', "reply_markup": {"inline_keyboard": [ '"${3}"' ]}' "$MSG_URL"
# JSON='"text":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}' # JSON='"text":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}'
} }
send_button() { send_button() {
@ -114,7 +125,7 @@ upload_file(){
[[ "$file" = *'..'* ]] && return # no directory traversal [[ "$file" = *'..'* ]] && return # no directory traversal
[[ "$file" = '.'* ]] && return # no hidden or relative files [[ "$file" = '.'* ]] && return # no hidden or relative files
if [[ "$file" = '/'* ]] ; then if [[ "$file" = '/'* ]] ; then
[[ ! "$file" =~ $FILE_REGEX ]] && return # absulute must match REGEX [[ ! "$file" =~ $FILE_REGEX ]] && return # absolute must match REGEX
else else
file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # othiers must be in UPLOADDIR file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # othiers must be in UPLOADDIR
fi fi
@ -189,8 +200,9 @@ send_message() {
[ -z "$2" ] && return [ -z "$2" ] && return
local text keyboard btext burl no_keyboard file lat long title address sent local text keyboard btext burl no_keyboard file lat long title address sent
text="$(sed <<< "${2}" 's/ mykeyboardend.*//;s/ *my[kfltab][a-z]\{2,13\}startshere.*//')$(sed <<< "${2}" -n '/mytextstartshere/ s/.*mytextstartshere//p')" text="$(sed <<< "${2}" 's/ mykeyboardend.*//;s/ *my[kfltab][a-z]\{2,13\}startshere.*//')$(sed <<< "${2}" -n '/mytextstartshere/ s/.*mytextstartshere//p')"
# shellcheck disable=SC2001 #shellcheck disable=SC2001
text="$(sed <<< "${text}" 's/ *mynewlinestartshere */\r\n/g')" text="$(sed <<< "${text}" 's/ *mynewlinestartshere */\\n/g')"
text="${text//$'\n'/\\n}"
[ "$3" != "safe" ] && { [ "$3" != "safe" ] && {
no_keyboard="$(sed <<< "${2}" '/mykeyboardendshere/!d;s/.*mykeyboardendshere.*/mykeyboardendshere/')" no_keyboard="$(sed <<< "${2}" '/mykeyboardendshere/!d;s/.*mykeyboardendshere.*/mykeyboardendshere/')"
keyboard="$(sed <<< "${2}" '/mykeyboardstartshere /!d;s/.*mykeyboardstartshere *//;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" keyboard="$(sed <<< "${2}" '/mykeyboardstartshere /!d;s/.*mykeyboardstartshere *//;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')"
@ -237,10 +249,10 @@ send_message() {
send_text() { send_text() {
case "$2" in case "$2" in
html_parse_mode*) 'html_parse_mode'*)
send_html_message "$1" "${2//html_parse_mode}" send_html_message "$1" "${2//html_parse_mode}"
;; ;;
markdown_parse_mode*) 'markdown_parse_mode'*)
send_markdown_message "$1" "${2//markdown_parse_mode}" send_markdown_message "$1" "${2//markdown_parse_mode}"
;; ;;
*) *)

View File

@ -8,7 +8,7 @@
# #### if you start to develop your own bot, use the clean version of this file: # #### if you start to develop your own bot, use the clean version of this file:
# #### mycommands.clean # #### mycommands.clean
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-pre-0-g03700cd
# #
# uncomment the following lines to overwrite info and help messages # uncomment the following lines to overwrite info and help messages
@ -23,8 +23,8 @@ export res=""
export INLINE="0" export INLINE="0"
# Set to .* to allow sending files from all locations # Set to .* to allow sending files from all locations
# NOTE: this is a regex, not shell globbing! you must use a valid egex, # NOTE: this is a regex, not shell globbing! you must use a valid egex,
# '.' matches any charater and '.*' matches all remaining charatcers! # '.' matches any character and '.*' matches all remaining charatcers!
# additionally you must escape special charaters with '\', e.g. '\. \? \[ \*" to match them literally # additionally you must escape special characters with '\', e.g. '\. \? \[ \*" to match them literally
export FILE_REGEX="${BASHBOT_ETC}/.*" export FILE_REGEX="${BASHBOT_ETC}/.*"
# example: run bashbot over TOR # example: run bashbot over TOR
# export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1:9050" # export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1:9050"
@ -37,7 +37,7 @@ unset BASHBOT_RETRY
# set value for adaptive sleeping while waitingnfor uodates in millisconds # set value for adaptive sleeping while waitingnfor uodates in millisconds
# max slepp between polling updates 10s (default 5s) # max slepp between polling updates 10s (default 5s)
export BASHBOT_SLEEP="10000" export BASHBOT_SLEEP="10000"
# add 0.2s if no update availble, up to BASHBOT_SLEEP (default 0.1s) # add 0.2s if no update available, up to BASHBOT_SLEEP (default 0.1s)
export BASHBOT_SLEEP_STEP="200" export BASHBOT_SLEEP_STEP="200"
# if you want to use timer functions, set BASHBOT_START_TImer to not empty value # if you want to use timer functions, set BASHBOT_START_TImer to not empty value
@ -62,8 +62,8 @@ if [ "$1" = "startbot" ];then
# reminde bot that it was started # reminde bot that it was started
touch .mystartup touch .mystartup
else else
# here we call the function above when the mesage arrives # here we call the function above when the message arrives
# things to do only at soure, eg. after startup # things to do only at source, eg. after startup
[ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup [ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup
############################# #############################
@ -82,6 +82,19 @@ else
fi fi
fi fi
# example for actions based on chat or sender
case "${USER[ID]}+${CHAT[ID]}" in
'USERID+'*) # do something for all messages from USER
printf "%s: U=%s C=%s M=%s\n" "$(date)" "${USER[ID]}" "${CHAT[ID]}" "${MESSAGE}" >>"${DATADIR}/${USER[ID]}.log"
;;&
*'+CHATID') # do something for all messages from CHAT
printf "%s: U=%s C=%s M=%s\n" "$(date)" "${USER[ID]}" "${CHAT[ID]}" "${MESSAGE}" >>"${DATADIR}/${CHAT[ID]}.log"
;;&
'USERID+CHATID') # do something only for messages form USER in CHAT
printf "%s: U=%s C=%s M=%s\n" "$(date)" "${USER[ID]}" "${CHAT[ID]}" "${MESSAGE}" >>"${DATADIR}/${CHAT[ID]}+${USER[ID]}.log"
;;&
esac
# pre-check admin only commands # pre-check admin only commands
case "${MESSAGE}" in case "${MESSAGE}" in
# must be private, group admin, or botadmin # must be private, group admin, or botadmin
@ -99,6 +112,12 @@ else
send_markdown_message "${CHAT[ID]}" "*${NOTBOTADMIN}*"; return 1 send_markdown_message "${CHAT[ID]}" "*${NOTBOTADMIN}*"; return 1
fi fi
;; ;;
# will we process edited messages also?
'/edited_message'*)
return 1 # no
# but if we do, remove /edited_message
MESSAGE="${MESSAGE#/* }"
;;
esac esac
case "${MESSAGE}" in case "${MESSAGE}" in
@ -153,7 +172,7 @@ else
myinlines() { myinlines() {
####################### #######################
# Inline query examples, do not use them in production (exept image search ;-) # Inline query examples, do not use them in production (except image search ;-)
# shellcheck disable=SC2128 # shellcheck disable=SC2128
iQUERY="${iQUERY,,}" # all lowercase iQUERY="${iQUERY,,}" # all lowercase
case "${iQUERY}" in case "${iQUERY}" in
@ -195,7 +214,7 @@ else
"sticker") # example chaecd telegram sticker "sticker") # example chaecd telegram sticker
answer_inline_query "${iQUERY[ID]}" "cached_sticker" "BQADBAAD_QEAAiSFLwABWSYyiuj-g4AC" answer_inline_query "${iQUERY[ID]}" "cached_sticker" "BQADBAAD_QEAAiSFLwABWSYyiuj-g4AC"
;; ;;
"gif") # exmaple chaehed gif "gif") # example cached gif
answer_inline_query "${iQUERY[ID]}" "cached_gif" "BQADBAADIwYAAmwsDAABlIia56QGP0YC" answer_inline_query "${iQUERY[ID]}" "cached_gif" "BQADBAADIwYAAmwsDAABlIia56QGP0YC"
;; ;;
esac esac

View File

@ -4,7 +4,7 @@
# files: mycommands.sh.clean # files: mycommands.sh.clean
# copy to mycommands.sh and add all your commands and functions here ... # copy to mycommands.sh and add all your commands and functions here ...
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# #
########## ##########
@ -22,8 +22,8 @@ export res=""
export INLINE="0" export INLINE="0"
# NOTE: this is a regex, not shell globbing! you must use a valid egex, # NOTE: this is a regex, not shell globbing! you must use a valid egex,
# '.' matches any charater and '.*' matches all remaining charatcers! # '.' matches any character and '.*' matches all remaining charatcers!
# additionally you must escape special charaters with '\', e.g. '\. \? \[ \*" to match them literally # additionally you must escape special characters with '\', e.g. '\. \? \[ \*" to match them literally
# do NOT set to .* as this allow sending files from all locations! # do NOT set to .* as this allow sending files from all locations!
export FILE_REGEX="${BASHBOT_ETC}/.*" export FILE_REGEX="${BASHBOT_ETC}/.*"
@ -35,7 +35,7 @@ unset BASHBOT_RETRY
# set value for adaptive sleeping while waitingnfor uodates in millisconds # set value for adaptive sleeping while waitingnfor uodates in millisconds
# max slepp between polling updates 10s (default 5s) # max slepp between polling updates 10s (default 5s)
export BASHBOT_SLEEP="10000" export BASHBOT_SLEEP="10000"
# add 0.2s if no update availble, up to BASHBOT_SLEEP (default 0.1s) # add 0.2s if no update available, up to BASHBOT_SLEEP (default 0.1s)
export BASHBOT_SLEEP_STEP="200" export BASHBOT_SLEEP_STEP="200"
# if you want to use timer functions, set BASHBOT_START_TImer to not empty value # if you want to use timer functions, set BASHBOT_START_TImer to not empty value
@ -59,8 +59,8 @@ if [ "$1" = "startbot" ];then
} }
touch .mystartup touch .mystartup
else else
# here we call the function above when the mesage arrives # here we call the function above when the message arrives
# things to do only at soure, eg. after startup # things to do only at source, eg. after startup
[ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup [ -f .mystartup ] && rm -f .mystartup && _exec_if_function my_startup
############################# #############################
@ -72,7 +72,6 @@ else
# a service Message was received # a service Message was received
# add your own stuff here # add your own stuff here
if [ -n "${SERVICE}" ]; then if [ -n "${SERVICE}" ]; then
# example: delete every service message # example: delete every service message
if [ "${SILENCER}" = "yes" ]; then if [ "${SILENCER}" = "yes" ]; then
delete_message "${CHAT[ID]}" "${MESSAGE[ID]}" delete_message "${CHAT[ID]}" "${MESSAGE[ID]}"

View File

@ -6,11 +6,11 @@
# This file is public domain in the USA and all free countries. # This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) # Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
###### ######
# parameters # parameters
# $1 $2 args as given to starct_proc chat srcipt arg1 arg2 # $1 $2 args as given to starct_proc chat script arg1 arg2
# $3 path to named pipe # $3 path to named pipe

View File

@ -2,7 +2,7 @@
# #
# ADD a new test skeleton to test dir, but does not activate test # ADD a new test skeleton to test dir, but does not activate test
# #
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# magic to ensure that we're always inside the root of our application, # magic to ensure that we're always inside the root of our application,
# no matter from which directory we'll run script # no matter from which directory we'll run script
@ -27,7 +27,7 @@ read -r PASS
# pass to lower, default pass d # pass to lower, default pass d
PASS="${PASS,,}" PASS="${PASS,,}"
[ "${PASS}" = "" ] && PASS="d" [ "${PASS}" = "" ] && PASS="d"
[ "${#PASS}" != '1' ] && echo "Sorry, PASS must exactly one charater from a to z, aborting ..." && exit 1 [ "${#PASS}" != '1' ] && echo "Sorry, PASS must exactly one character from a to z, aborting ..." && exit 1
TEST="${PASS}-${NAME}-test" TEST="${PASS}-${NAME}-test"

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# include common functions and definitions # include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh # shellcheck source=test/ALL-tests.inc.sh
@ -17,7 +17,7 @@ botadmin
EOF EOF
echo "${SUCCESS}" echo "${SUCCESS}"
# compare files with refrence files # compare files with reference files
echo "Check new files after init ..." echo "Check new files after init ..."
export FAIL="0" export FAIL="0"
for file in ${TESTFILES} for file in ${TESTFILES}

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# include common functions and definitions # include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh # shellcheck source=test/ALL-tests.inc.sh
@ -25,12 +25,12 @@ UPDATE="$(cat "${INPUTFILE}")"
declare -A UPD declare -A UPD
source <( printf 'UPD=( %s )' "$(sed <<<"${UPDATE}" -E -e 's/\t/=/g' -e 's/=(true|false)/="\1"/')" ) source <( printf 'UPD=( %s )' "$(sed <<<"${UPDATE}" -E -e 's/\t/=/g' -e 's/=(true|false)/="\1"/')" )
# run process_message with and without phyton # run process_message with and without python
echo "Check process_inline ..." echo "Check process_inline ..."
for i in 1 2 for i in 1 2
do do
[ "${i}" = "1" ] && ! command -v python >/dev/null 2>&1 && continue [ "${i}" = "1" ] && ! command -v python >/dev/null 2>&1 && continue
[ "${i}" = "1" ] && echo " ... with JsonDecode Phyton" && unset BASHBOT_DECODE [ "${i}" = "1" ] && echo " ... with JsonDecode Python" && unset BASHBOT_DECODE
[ "${i}" = "2" ] && echo " ... with JsonDecode Bash" && export BASHBOT_DECODE="yes" [ "${i}" = "2" ] && echo " ... with JsonDecode Bash" && export BASHBOT_DECODE="yes"
set -x set -x
{ process_inline "0"; set +x; } >>"${LOGFILE}" 2>&1; { process_inline "0"; set +x; } >>"${LOGFILE}" 2>&1;

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#### $$VERSION$$ v0.96-0-g3871ca9 #### $$VERSION$$ v0.98-dev-70-g694ee61
# include common functions and definitions # include common functions and definitions
# shellcheck source=test/ALL-tests.inc.sh # shellcheck source=test/ALL-tests.inc.sh
@ -26,7 +26,7 @@ mkdir -p "${BASHBOT_ETC}" || exit 1
mkdir -p "${BASHBOT_VAR}" || exit 1 mkdir -p "${BASHBOT_VAR}" || exit 1
mkdir -p "${BASHBOT_BIN}" || exit 1 mkdir -p "${BASHBOT_BIN}" || exit 1
# cp bashbot files to new localtions # cp bashbot files to new locations
set +f set +f
# shellcheck disable=SC2086 # shellcheck disable=SC2086
cp ${TESTDIR}/*commands.sh "${BASHBOT_ETC}" || exit 1 cp ${TESTDIR}/*commands.sh "${BASHBOT_ETC}" || exit 1
@ -64,7 +64,7 @@ echo " ... BASHBOT_VAR seems to work!"
echo "${SUCCESS}" echo "${SUCCESS}"
# compare files with refrence files # compare files with reference files
export FAIL="0" export FAIL="0"
for file in ${TESTFILES} for file in ${TESTFILES}
do do