Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ Thumbs.db
# For example, if you have a vendor directory for C dependencies:
# /vendor/

immo
immo
config.toml
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ nostr-relay-builder = "0.44.0"
serde = "1.0.228"
tokio = { version = "1.48.0", features = ["full"] }
toml = "0.9.8"
teloxide = { version = "0.12", features = ["macros"] }
log = "0.4"
pretty_env_logger = "0.5"
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ cargo run --release

The relay can be configured using a `config.toml` file. See the example configuration in the repository.

#### Telegram Bot Integration

Immortal includes optional Telegram bot integration for monitoring and notifications. To enable the bot:

1. Create a new bot with [@BotFather](https://t.me/botfather) on Telegram
2. Get your bot token
3. Add the token to your `config.toml`:

```toml
telegram_bot_token = "YOUR_BOT_TOKEN_HERE"
```

The bot supports the following commands:
- `/start` - Welcome message
- `/status` - Get relay status
- `/help` - Show available commands

If no token is provided, the relay will run without the Telegram bot.

## Updates

Updates, changes, or community discussions can be followed on the GitHub issue/discussion or the [Dezh Technologies Nostr profile](https://njump.me/dezh.tech).
Expand Down
6 changes: 6 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
port = 7777
working_dir = "immo"
max_reqs = 128
notes_per_minute = 30
telegram_bot_token = "1234567890:ABCdefGHIjklMNOpqrsTUVwxyZ"
telegram_group_chat_id = -1001234567890
2 changes: 0 additions & 2 deletions config.toml

This file was deleted.

8 changes: 7 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ use std::fs;

pub fn load(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
let s = fs::read_to_string(path)?;
let cfg: Config = toml::from_str(&s)?;
let mut cfg: Config = toml::from_str(&s)?;

// Override with environment variable if provided
if let Ok(token) = std::env::var("TELEGRAM_BOT_TOKEN") {
cfg.telegram_bot_token = Some(token);
}

Ok(cfg)
}
4 changes: 4 additions & 0 deletions src/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ use serde::Deserialize;
pub struct Config {
pub working_dir: String,
pub port: u16,
pub max_reqs: usize,
pub notes_per_minute: u32,
pub telegram_bot_token: Option<String>,
pub telegram_group_chat_id: Option<i64>,
}
81 changes: 77 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,100 @@
use nostr_lmdb::NostrLMDB;
use nostr_relay_builder::prelude::*;
use std::sync::Arc;
use tokio::sync::mpsc;

mod config;
mod manager;
mod policies;

use manager::TelegramBot;
use policies::reports::ReportNotification;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
log::info!("Starting Immortal...");

let cfg = config::load("config.toml")?;
let database = NostrLMDB::open(format!("{}/database", cfg.working_dir))?;

let bot_database = NostrLMDB::open(format!("{}/database", cfg.working_dir))?;
let database_arc = Arc::new(bot_database);

let (report_tx, mut report_rx) = mpsc::unbounded_channel::<ReportNotification>();
let report_sender = Arc::new(report_tx);

let report_policy = policies::ReportEvents::new().with_report_sender(report_sender);

let builder = RelayBuilder::default()
.port(cfg.port)
.database(database)
.rate_limit(RateLimit {
max_reqs: 128,
notes_per_minute: 30,
});
max_reqs: cfg.max_reqs,
notes_per_minute: cfg.notes_per_minute,
})
.write_policy(report_policy);

let relay = LocalRelay::new(builder);

relay.run().await?;
log::info!("Relay listening on {}", relay.url().await);

let bot_handle = if let Some(token) = &cfg.telegram_bot_token {
log::info!("Starting Telegram bot...");
let bot = Arc::new(TelegramBot::new(
token.clone(),
cfg.telegram_group_chat_id,
database_arc.clone(),
));

println!("Relay listening on {}", relay.url().await);
let bot_for_commands = bot.clone();
let bot_for_notifications = bot.clone();

let commands_handle = tokio::spawn(async move {
log::info!("Telegram bot command handler starting...");
if let Err(e) = bot_for_commands.handle_commands().await {
log::error!("Telegram bot error: {}", e);
}
});

let notifications_handle = tokio::spawn(async move {
log::info!("Notification handler started, waiting for reports...");
while let Some(notification) = report_rx.recv().await {
log::info!(
"📨 Received report notification for event: {}",
notification.event_id
);

match bot_for_notifications
.send_report_notification(&notification.event_id, &notification.message)
.await
{
Ok(()) => {
log::info!("✅ Successfully sent Telegram notification");
}
Err(e) => {
log::error!("❌ Failed to send Telegram notification: {}", e);
}
}
}
log::warn!("Notification handler loop ended!");
});

Some((commands_handle, notifications_handle))
} else {
log::info!("No Telegram bot token provided, bot will not start");
None
};

tokio::signal::ctrl_c().await?;

log::info!("Shutting down...");

if let Some((commands_handle, notifications_handle)) = bot_handle {
commands_handle.abort();
notifications_handle.abort();
}

Ok(())
}
3 changes: 3 additions & 0 deletions src/manager/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod telegram_bot;

pub use telegram_bot::TelegramBot;
Loading