Read config/db paths from env, plus add docs

master
Manos Pitsidianakis 2022-09-24 17:18:50 +03:00
parent 908493327f
commit bb718d51e0
7 changed files with 193 additions and 77 deletions

View File

@ -2,6 +2,26 @@
A bot to handle bug reports through mail, for gitea's issue tracker.
Expects configuration file path in environment variable `ISSUE_BOT_CONFIG`. If
it's not defined, default is `./config.toml` (current working directory).
Expects database file path in environment variable `ISSUE_BOT_DB`. If it's not
defined, default is `./sqlite3.db` (current working directory).
```
issue-bot
```
by default expects to read a valid [RFC5322](https://www.rfc-editor.org/rfc/rfc5322) e-mail in valid utf-8 or valid ascii.
```
issue-bot cron
```
Checks if there are new comments or other updates in the issues, and sends
emails to anyone subscribed. An example systemd service and timer file is provided in `docs/`.
## Problem
Users have to register to your gitea instance to file bugs. This is a deterrent. A mailing list requires less effort, but lacks bridging with an issue tracker.
@ -18,9 +38,9 @@ Spam?
## Configuration
The bot looks for a `config.toml` file in the same directory as the binary. The config needs the following values:
The config file must be valid TOML and needs the following values:
```text
```toml
# the tag that prefixes email subjects eg "[issue-bot-issues] blah blah"
tag = "issue-bot-issues"
# the auth_token for the gitea's instance API
@ -38,68 +58,12 @@ bot_username = "issue_bot"
mailer = "/usr/sbin/sendmail -t"
```
Optionally, you can set `dry_run = true` to avoid any email/db update being performed in order to debug what would happen if you ran the `cron` command.
Setup your mail server to deliver mail with destination `{local_part}+tags@{domain}` to this binary. Simply call the binary and write the email in UTF-8 in the binary's standard input.
On postfix this can be done by creating a transport map and a pipe. A transport map is a file that tells postfix to send mails send to `{local_part}` to a specific program. The pipe will be this program.
For postfix setup see `docs/POSTFIX.md`.
Open `master.cf` and paste this line at the bottom:
```text
issue_bot unix - n n - - pipe
user=issuebot directory=/path/to/binarydir/ argv=/path/to/binary
```
an example:
```text
issue_bot unix - n n - - pipe
user=issuebot directory=/home/issuebot/ argv=/home/issuebot/issue-bot
```
Then create your transport map:
```text
{local_part}@{domain} issue_bot:
```
Notice the colon at the end. This means that it refers to a transfer, not an address. Save the file somewhere (eg `/etc/postfix/issue_transport`) and make it readable by postfix. Issue `postmap /etcpostfix/issue_transport`. Finally add the entry `hash:/etc/postfix/issue_transport` in your `transport_maps` and `local_recipient_maps` keys in `main.cf`. `postfix reload` to load the configuration changes.
You will also need the following setting to allow tags in your recipient addresses:
```text
recipient_delimiter = +
```
Setup a periodic check in your preferred task scheduler to run `issue_bot_bin cron` in order to fetch replies to issues. On systemd this can be done with timers.
### Troubleshooting
If you your email stops working or postfix doesn't pass mail to the bot, make sure you're not using a non-default setup like virtual mailboxes. In that case you have to add the transport along with the transports of your setup, whatever that be.
If the e-mail gets to the binary and nothing happens, make sure:
- the binary is executable and readable by the pipe's user
- the configuration file is in the same directory as the binary
- that in `master.cf` there are no `flags=` in the transport entry. The mail must be piped unaltered.
- your auth token works. You can check yourself by issuing requests to your API via cURL. There are examples here: https://docs.gitea.io/en-us/api-usage/
If commands (using +reply, +close etc) don't work, make sure you have added `recipient_delimiter = +` in your `main.cf` file.
The bot's state is saved in a sqlite3 database in the same directory as the binary. You can view its data by using the `sqlite3` cli tool:
```shell
root# sqlite3 /home/issuebot/sqlite3.db
SQLite version ****** ********** ********
Enter ".help" for usage hints.
sqlite> .tables
issue
sqlite> select * from issue;
1|Name <add@res.tld>|1F:|2019-09-29T12:20:21.658495173Z|0|1|issue title|"2019-09-29T15:20:21+03:00"
2|Name <add@res.tld>|{^D0u|2019-09-29T12:23:48.291970808Z|0|1|issue title#2|"2019-09-29T15:23:48+03:00"
3|Name <add@res.tld>|Gd)i]|2019-09-29T12:24:31.414792595Z|0|1|issue title again|"2019-09-29T15:26:53+03:00"
4|Name <add@res.tld>|$3fBוv|2019-09-29T12:28:21.187425505Z|1|1|many issues|"2019-09-29T15:28:21+03:00"
```
## Demo
My email:

73
docs/POSTFIX.md 100644
View File

@ -0,0 +1,73 @@
# issue-bot with postfix
Setup your mail server to deliver mail with destination `{local_part}+tags@{domain}` to this binary. Simply call the binary and write the email in UTF-8 in the binary's standard input.
On postfix this can be done by creating a transport map and a pipe. A transport map is a file that tells postfix to send mails send to `{local_part}` to a specific program. The pipe will be this program.
**BEWARE**: If `issue-bot` needs to read its configuration file and database file paths from environment variables, create a wrapper script and call that from postfix instead of going through the complicated trouble of setting up the exported environment (see postfix manual pages `master(t)` and `pipe(8)`)
```shell
/bin/sh
export ISSUE_BOT_CONFIG=_
export ISSUE_BOT_DB=_
/path/to/issue-bot
```
Open `master.cf` and paste this line at the bottom:
```text
issue_bot unix - n n - - pipe
user=issuebot directory=/path/to/binarydir/ argv=/path/to/binary
```
an example:
```text
issue_bot unix - n n - - pipe
user=issuebot directory=/home/issuebot/ argv=/home/issuebot/issue-bot
```
Then create your transport map:
```text
{local_part}@{domain} issue_bot:
```
Notice the colon at the end. This means that it refers to a transfer, not an address. Save the file somewhere (eg `/etc/postfix/issue_transport`) and make it readable by postfix. Issue `postmap /etcpostfix/issue_transport`. Finally add the entry `hash:/etc/postfix/issue_transport` in your `transport_maps` and `local_recipient_maps` keys in `main.cf`. `postfix reload` to load the configuration changes.
You will also need the following setting to allow tags in your recipient addresses:
```text
recipient_delimiter = +
```
Setup a periodic check in your preferred task scheduler to run `issue_bot_bin cron` in order to fetch replies to issues. On systemd this can be done with timers.
### Troubleshooting
If you your email stops working or postfix doesn't pass mail to the bot, make sure you're not using a non-default setup like virtual mailboxes. In that case you have to add the transport along with the transports of your setup, whatever that be.
If the e-mail gets to the binary and nothing happens, make sure:
- the binary is executable and readable by the pipe's user
- the configuration file is in the same directory as the binary
- that in `master.cf` there are no `flags=` in the transport entry. The mail must be piped unaltered.
- your auth token works. You can check yourself by issuing requests to your API via cURL. There are examples here: https://docs.gitea.io/en-us/api-usage/
If commands (using +reply, +close etc) don't work, make sure you have added `recipient_delimiter = +` in your `main.cf` file.
The bot's state is saved in a sqlite3 database in the same directory as the binary. You can view its data by using the `sqlite3` cli tool:
```shell
root# sqlite3 /home/issuebot/sqlite3.db
SQLite version ****** ********** ********
Enter ".help" for usage hints.
sqlite> .tables
issue
sqlite> select * from issue;
1|Name <add@res.tld>|1F:|2019-09-29T12:20:21.658495173Z|0|1|issue title|"2019-09-29T15:20:21+03:00"
2|Name <add@res.tld>|{^D0u|2019-09-29T12:23:48.291970808Z|0|1|issue title#2|"2019-09-29T15:23:48+03:00"
3|Name <add@res.tld>|Gd)i]|2019-09-29T12:24:31.414792595Z|0|1|issue title again|"2019-09-29T15:26:53+03:00"
4|Name <add@res.tld>|$3fBוv|2019-09-29T12:28:21.187425505Z|1|1|many issues|"2019-09-29T15:28:21+03:00"
```

34
docs/SCHEDULING.md 100644
View File

@ -0,0 +1,34 @@
# issue-bot scheduled jobs
You can set up scheduled jobs by configuring `crontab` to run `issue-bot cron` whenever you want. For the more complicated but more reliable systemd setup, two example files are included in this directory: a service unit file and a timer unit file. The service unit executes once, and the timer unit is responsible for calling the service at the intervals you set.
Copy the example files somewhere else and edit them with your own values.
You can put `dry_run = true` in the config file to check it works without making changes or sending any mail. Also, backup your database if needed.
```shell
systemctl --user enable issue-bot.service
```
You can do a test run with
```shell
systemctl --user start issue-bot.service
```
Now you enable/activate the timer.
```shell
systemctl --user enable issue-bot.timer
```
```shell
systemctl --user start issue-bot.timer
```
Monitor the service status:
```shell
systemctl --user status issue-bot
```

View File

@ -0,0 +1,14 @@
[Unit]
Description=issue-bot cron
RefuseManualStart=no # Allow manual starts
RefuseManualStop=no # Allow manual stops
[Service]
Type=simple
ExecStart=/path/to/issue-bot cron
Environment=ISSUE_BOT_CONFIG=/a/b/c/d.toml
Environment=ISSUE_BOT_DB=/a/b/c/sqlite3.db
[Install]
WantedBy=default.target

View File

@ -0,0 +1,29 @@
[Unit]
Description=issue-bot crons
RefuseManualStart=no # Allow manual starts
RefuseManualStop=no # Allow manual stops
[Timer]
#Execute job if it missed a run due to machine being off
Persistent=true
#Run 120 seconds after boot for the first time
OnBootSec=120
#Run every 5 minutes thereafter
OnUnitActiveSec=300
#File describing job to execute
Unit=issue-bot.service
## more complicated examples:
# # run on the minute of every minute every hour of every day
# OnCalendar=*-*-* *:*:00
# # run on the hour of every hour of every day
# OnCalendar=*-*-* *:00:00
# # run every day
# OnCalendar=*-*-* 00:00:00
# # run 11:12:13 of the first or fifth day of any month of the year
# # 2012, but only if that day is a Thursday or Friday
# OnCalendar=Thu,Fri 2012-*-1,5 11:12:13
[Install]
WantedBy=timers.target

View File

@ -122,15 +122,13 @@ pub fn check(conn: Connection, conf: Configuration) -> Result<()> {
for issue in results {
errors.push(check_issue(&conn, &conf, issue));
}
if errors.iter().any(|r| matches!(r, Ok(true))) {
let successes_count = errors.iter().filter(|r| matches!(r, Ok(true))).count();
let error_count = errors.iter().filter(|r| r.is_err()).count();
log::info!(
"Cron run with {} updates and {} errors.",
successes_count,
error_count
);
}
let successes_count = errors.iter().filter(|r| matches!(r, Ok(true))).count();
let error_count = errors.iter().filter(|r| r.is_err()).count();
log::info!(
"Cron run with {} updates and {} errors.",
successes_count,
error_count
);
_ = errors.into_iter().collect::<Result<Vec<bool>>>()?;
Ok(())
}

View File

@ -300,7 +300,9 @@ fn run_request(conn: Connection, conf: Configuration) -> Result<()> {
}
fn run_app() -> Result<()> {
let mut file = std::fs::File::open("./config.toml")?;
let conf_path =
std::env::var("ISSUE_BOT_CONFIG").unwrap_or_else(|_| "./config.toml".to_string());
let mut file = std::fs::File::open(&conf_path)?;
let args = std::env::args().skip(1).collect::<Vec<String>>();
let perform_cron: bool;
if args.len() > 1 {
@ -339,11 +341,11 @@ fn run_app() -> Result<()> {
*
*
*/
let db_path = "./sqlite3.db";
let conn = Connection::open(db_path)?;
let db_path = std::env::var("ISSUE_BOT_DB").unwrap_or_else(|_| "./sqlite3.db".to_string());
let conn = Connection::open(&db_path)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS issue (
conn.execute_batch(
r##"CREATE TABLE IF NOT EXISTS issue (
id INTEGER PRIMARY KEY,
submitter TEXT NOT NULL,
password BLOB,
@ -352,8 +354,10 @@ fn run_app() -> Result<()> {
subscribed BOOLEAN,
title TEXT NOT NULL,
last_update TEXT
)",
[],
);
UPDATE issue SET last_update = replace(last_update, '"', '');
"##,
)?;
if perform_cron {