Skip to content

Commit

Permalink
Add documentation links for important code pieces
Browse files Browse the repository at this point in the history
  • Loading branch information
reimarstier committed Jun 24, 2024
1 parent 7c401d5 commit 98477cf
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 15 deletions.
30 changes: 22 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@

* Learn rust
* Evaluate rust [teloxide](https://github.com/teloxide/teloxide) crate to write a telegram bot
* Examples [here](https://github.com/teloxide/teloxide/tree/master/crates/teloxide/examples)
* Teloxide uses [dptree](https://github.com/teloxide/dptree) -
its own implementation of the [chain (tree) of responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).
This makes writing handlers very easy, but it has some pitfalls.
* Create an elaborate **example** project
* Production ready
* Health check endpoint
* Self-hosted deployment example
* Allow to differentiate users (registered users, privileged users aka admins)
* Production ready (when using telegram bot with webhook)
* Moved bot url to own path `/bot` when using webhook to allow additional [endpoints](src/bot/core/dispatch.rs).
* Own health check endpoint `/healthcheck`
* Check telegram api for bot health (introspection), [here](src/bot/core/healthcheck/tasks/webhook.rs).
* Self-hosted deployment [example](docker/build/docker-compose.yml)
* Features:
* User dialogues, remember state of dialogue
* User registration (identify and recognize known users)
Expand All @@ -26,11 +33,18 @@
Admin commands:
/broadcast — Send a message to all registered users.
```
* Example handlers:
* `/purchase` - Example from [here](https://github.com/teloxide/teloxide/blob/master/crates/teloxide/examples/purchase.rs)
* `/broadcast` - Broadcast messages to known users (ADMIN)

* CLI to add users
* Example handlers:
* `/register` [handler](src/bot/handlers/register.rs).
* `/purchase` [handler](src/bot/handlers/product.rs). Example from [here](https://github.com/teloxide/teloxide/blob/master/crates/teloxide/examples/purchase.rs).
* `/broadcast` [handler](src/bot/handlers/broadcast.rs). Broadcast messages to known users (only ADMIN).

* CLI
* Add user subcommand `cargo run -- admin`. [admin](src/bot/admin/mod.rs)
* Start bot in dev mode: `cargo run -- dev`
* SQLite database to store users
* Database [schema](src/bot/core/db/schema.rs)
* Rust database [model](src/bot/core/db/model.rs)
* Logging to file and log rotation, see [logging configuration](src/bot/core/util.rs)

## Getting started

Expand Down
2 changes: 1 addition & 1 deletion docker/addfromhost/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y libssl3 tzdata ca-certificates

# does not compile the app itself, simply adds the release binary from the host
# does not compile the app itself, simply adds the release binary from the host, requires compilation on the host
COPY ./target/release/rust-telegram-alias-bot /rust-telegram-alias-bot

# drop root privileges
Expand Down
2 changes: 2 additions & 0 deletions docker/addfromhost/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# it is important to ignore the large build context to reduce build time
.git/
target/debug/
# omitting release directory, so the docker build may find the release binary build from the host system
# ./target/release/
target/release/.fingerprint/
target/release/deps/
target/release/incremental/
Expand Down
12 changes: 11 additions & 1 deletion docker/addfromhost/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,29 @@ services:
ports:
- 8080:8080
environment:
# default configuration
- "TELOXIDE_LOG_DIR=/var/log/telegrambot/"
- "TELOXIDE_DATA_DIR=/var/lib/telegrambot/"
- "DATABASE_URL=sqlite:///var/lib/telegrambot/sqlite.db"
- "TELOXIDE_BIND_PORT=8080"
# user provided configuration via .env
- TELOXIDE_PUBLIC_URL
- TELOXIDE_TOKEN
volumes:
- ./log/:/var/log/telegrambot/
- ../../.env:/.env
- ./data/:/var/lib/telegrambot/
# in case you use traefik
labels:
- traefik.enable=true
- traefik.http.routers.telegrambot.rule=Host(`bot.example.org`)
- traefik.http.routers.telegrambot.tls=true
- traefik.http.services.telegrambot.loadbalancer.server.port=8080

# in case you use another reverse proxy, e.g. nginx
networks:
telegramnet:
# static ip for reverse proxy (e.g. nginx/traefik)
# static ip for reverse proxy (e.g. nginx)
ipv4_address: 192.168.16.200

networks:
Expand Down
33 changes: 31 additions & 2 deletions docker/build/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,36 @@ services:
ports:
- 8080:8080
environment:
- "TELOXIDE_LOG_DIR=/var/log/telegrambot/"
# default configuration
- "TELOXIDE_LOG_DIR=/var/log/telegrambot/"
- "TELOXIDE_DATA_DIR=/var/lib/telegrambot/"
- "DATABASE_URL=sqlite:///var/lib/telegrambot/sqlite.db"
- "TELOXIDE_BIND_PORT=8080"
# user provided configuration via .env
- TELOXIDE_PUBLIC_URL
- TELOXIDE_TOKEN
volumes:
# - ./log/:/var/log/telegrambot/
- ./log/:/var/log/telegrambot/
- ../../.env:/.env
- ./data/:/var/lib/telegrambot/
# in case you use traefik
labels:
- traefik.enable=true
- traefik.http.routers.telegrambot.rule=Host(`bot.example.org`)
- traefik.http.routers.telegrambot.tls=true
- traefik.http.services.telegrambot.loadbalancer.server.port=8080

# in case you use another reverse proxy, e.g. nginx
networks:
telegramnet:
# static ip for reverse proxy (e.g. nginx)
ipv4_address: 192.168.16.200

networks:
telegramnet:
name: telegram_network
ipam:
driver: default
config:
- subnet: 192.168.16.0/24
ip_range: 192.168.16.0/24
1 change: 1 addition & 0 deletions src/bot/core/bot_config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const TELOXIDE_BIND_PORT_KEY: &str = "TELOXIDE_BIND_PORT";
const TELOXIDE_BIND_ADDRESS_KEY: &str = "TELOXIDE_BIND_ADDRESS";
const TELOXIDE_PUBLIC_URL_KEY: &str = "TELOXIDE_PUBLIC_URL";
pub const TELOXIDE_BOT_NAME_KEY: &str = "TELOXIDE_BOT_NAME";
pub const TELEGRAM_BOT_ENDPOINT_HEALTHCHECK: &str = "/healthcheck";
const DATABASE_FILE_NAME: &str = "db.sqlite";

#[derive(Deserialize, Debug, Clone)]
Expand Down
4 changes: 2 additions & 2 deletions src/bot/core/bot_config/webhook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use anyhow::anyhow;
use std::net::SocketAddr;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use crate::bot::core::bot_config::{TELOXIDE_BIND_ADDRESS_KEY, TELOXIDE_BIND_PORT_KEY, TELOXIDE_PUBLIC_URL_KEY};
use crate::bot::core::bot_config::{TELEGRAM_BOT_ENDPOINT_HEALTHCHECK, TELOXIDE_BIND_ADDRESS_KEY, TELOXIDE_BIND_PORT_KEY, TELOXIDE_PUBLIC_URL_KEY};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub(crate) struct BotConfigWebHook {
Expand Down Expand Up @@ -34,7 +34,7 @@ impl BotConfigWebHook {
.map_err(|error| anyhow!("Failed to parse public url in environment variable '{}'. Error: {}", TELOXIDE_PUBLIC_URL_KEY, error))?;

let public_bot_url: Url = public_url.join("/bot")?;
let public_healthcheck_url: Url = public_url.join("/healthcheck")?;
let public_healthcheck_url: Url = public_url.join(TELEGRAM_BOT_ENDPOINT_HEALTHCHECK)?;

Ok(Self {
socket_address,
Expand Down
3 changes: 2 additions & 1 deletion src/bot/core/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::convert::Infallible;
use teloxide::prelude::Requester;
use teloxide::update_listeners::UpdateListener;
use teloxide::update_listeners::webhooks::{axum_to_router, Options};
use crate::bot::core::bot_config::TELEGRAM_BOT_ENDPOINT_HEALTHCHECK;
use crate::bot::core::healthcheck::endpoint::healthcheck_endpoint;

pub async fn axum_update_listener<R>(
Expand All @@ -16,7 +17,7 @@ pub async fn axum_update_listener<R>(

let (mut update_listener, stop_flag, app) = axum_to_router(bot, options).await?;
let my_router = axum::Router::new()
.route("/healthcheck", axum::routing::get(healthcheck_endpoint))
.route(TELEGRAM_BOT_ENDPOINT_HEALTHCHECK, axum::routing::get(healthcheck_endpoint))
.fallback_service(app);

let stop_token = update_listener.stop_token();
Expand Down

0 comments on commit 98477cf

Please sign in to comment.