commit f82c903e5ab4b2749cdd0c9350e7c93c8706c8a7
Author: Daniel Pérez <steew@psi.my.domain>
Date: Tue Jan 06 23:07:34 2026 +0000
diff --git a/.gitignore b/.gitignore
index 2f7896d..f8df8af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +13 @@
target/
+*~
+config.ini
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 54a025c..4f1b3a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -626 +627 @@ dependencies = [
"env",
"futures-util",
"irc",
+ "rust-ini",
"serenity",
"tokio",
]
@@ -1816 +18226 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom 0.2.16",
+ "once_cell",
+ "tiny-keccak",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -2306 +25112 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -2916 +31815 @@ dependencies = [
"syn 2.0.113",
]
+[[package]]
+name = "dlv-list"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
+dependencies = [
+ "const-random",
+]
+
[[package]]
name = "encoding"
version = "0.2.33"
@@ -10666 +110216 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "ordered-multimap"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
+dependencies = [
+ "dlv-list",
+ "hashbrown 0.14.5",
+]
+
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -13716 +141716 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rust-ini"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
[[package]]
name = "rustc-hash"
version = "2.1.1"
@@ -18496 +190515 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
[[package]]
name = "tinystr"
version = "0.8.2"
diff --git a/Cargo.toml b/Cargo.toml
index 266b5c9..ff2260e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -75 +76 @@ edition = "2024"
env = "1.0.1"
futures-util = "0.3.31"
irc = "1.1.0"
+rust-ini = "0.21.3"
serenity = "0.12.5"
tokio = { version = "1.49.0", features = ["rt-multi-thread"] }
diff --git a/src/main.rs b/src/main.rs
index d9b1451..05aeb81 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17 +110 @@
+use std::collections::HashMap;
use std::collections::VecDeque;
+use std::str::FromStr;
use std::sync::Arc;
use futures_util::StreamExt;
+use ini::Ini;
use irc::client::Sender;
use irc::client::data::Config;
use irc::proto::Command;
@@ -127 +156 @@ use serenity::all::Message;
use serenity::all::Ready;
use serenity::all::Webhook;
use serenity::async_trait;
-use serenity::model::webhook;
use serenity::prelude::*;
use tokio::spawn;
use tokio::sync::Notify;
@@ -208 +228 @@ use tokio::sync::Notify;
#[derive(Debug)]
enum RelayDirection {
- IRC2DIS,
- DIS2IRC,
+ IRC2DIS(String),
+ DIS2IRC(ChannelId),
}
struct RelayMessage {
@@ -466 +4822 @@ impl Default for RelayNotify {
}
+struct RelayAssoc {
+ // stores the Discord - IRC channel bridge associations
+ bridge_assoc: HashMap<ChannelId, Vec<String>>,
+ // stores the webhook URL for the discord channels
+ chid_webhook_assoc: HashMap<ChannelId, String>,
+}
+
+impl Default for RelayAssoc {
+ fn default() -> Self {
+ RelayAssoc {
+ bridge_assoc: HashMap::new(),
+ chid_webhook_assoc: HashMap::new()
+ }
+ }
+}
+
impl Default for RelayMessage {
@@ -907 +1087 @@ impl EventHandler for Handler {
- new_message.direction = RelayDirection::DIS2IRC;
+ new_message.direction = RelayDirection::DIS2IRC(msg.channel_id);
@@ -1136 +1317 @@ async fn relay_consumer(
+ assoc: RelayAssoc
) {
@@ -12713 +14613 @@ async fn relay_consumer(
- RelayDirection::IRC2DIS => {
+ RelayDirection::IRC2DIS(chan) => {
- RelayDirection::DIS2IRC => {
+ RelayDirection::DIS2IRC(chan) => {
@@ -14939 +16879 @@ async fn relay_consumer(
}
+async fn irc_producer(
+ mut irc_client: irc::client::Client,
+ buffer_reference: Arc<RwLock<MessageBuffer>>,
+ notify: Arc<RelayNotify>
+) {
+ let mut irc_stream: irc::client::ClientStream = irc_client.stream().unwrap();
+
+ while let Ok(Some(message)) = irc_stream.next().await.transpose() {
+ let msg_clone = message.clone();
+ match message.command {
+ Command::PRIVMSG(_, contents) => {
+ {
+ let uname = msg_clone.source_nickname().unwrap();
+ let mut buffer = buffer_reference.write().await;
+ let mut new_message = RelayMessage::default();
+ new_message.contents = contents;
+ new_message.direction = RelayDirection::IRC2DIS(String::from_str(msg_clone.response_target().unwrap()).unwrap());
+ new_message.author.push_str(uname);
+ buffer.pending_relay_messages.push_back(new_message);
+ notify.notify.notify_one();
+ }
+ println!("Added IRC message to buffer");
+ }
+ _ => {}
+ }
+ }
+
+}
#[tokio::main]
async fn main() {
- let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
+ // Shared data initialization
+ // ======================================================================================
+ let buffer_reference = Arc::new(RwLock::new(MessageBuffer::default()));
+ let notify = Arc::new(RelayNotify::default());
+
+ let discord_notify = notify.clone();
+ let discord_buffer_reference = buffer_reference.clone();
+ let irc_notify = notify.clone();
+ let irc_buffer_reference = buffer_reference.clone();
+ // Discord initialization
+ // ======================================================================================
+ let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
+
- let buffer_reference = Arc::new(RwLock::new(MessageBuffer::default()));
- let notify = Arc::new(RelayNotify::default());
-
- data.insert::<MessageBuffer>(buffer_reference.clone());
- data.insert::<RelayNotify>(notify.clone());
+ data.insert::<MessageBuffer>(discord_buffer_reference);
+ data.insert::<RelayNotify>(discord_notify);
-
+
+ // shared http sender for the discord client
- let shared_notify = notify.clone();
- let shared_buffer_reference = buffer_reference.clone();
-
- let webhook = Webhook::from_url(&shared_http, "").await.unwrap();
-
- // spawn discord client
+
+ // spawn discord client async thread
+ // TODO: remove and read from file
+ let webhook = Webhook::from_url(&shared_http,
+ "").await.unwrap();
+
+ // ======================================================================================
+ // IRC initialization
@@ -18940 +24846 @@ async fn main() {
- let mut irc_client = irc::client::Client::from_config(config).await.unwrap();
+ let irc_client = irc::client::Client::from_config(config).await.unwrap();
+ let irc_sender = irc_client.sender().clone();
- let mut irc_stream: irc::client::ClientStream = irc_client.stream().unwrap();
+ let _irc_handle = spawn(async move {
+ irc_producer(irc_client, irc_buffer_reference, irc_notify).await;
+ });
+ // Configuration file read for channel bridge associations
+ // ======================================================================================
+ let ini_conf_file = "config.ini";
+ let ini = Ini::load_from_file(ini_conf_file).expect("A config.ini file is needed");
+
+ let mut assoc = RelayAssoc::default();
+
+ match ini.section(Some("assoc")) {
+ Some(section_contents) => {
+ for (d_channel, i_channel) in section_contents.iter() {
+ let chid = u64::from_str(d_channel).expect("Channel ID is not valid! {d_channel}");
+ let irc_chan = String::from_str(i_channel).expect("Channel name is not valid! {i_channel}");
+ let mut ircs = Vec::new();
+ ircs.push(irc_chan);
+ assoc.bridge_assoc.insert(ChannelId::new(chid), ircs);
+ }
+ },
+ None => { panic!("Expected an [assoc] section with bridge associations.") },
+ }
+ println!("{:?}", assoc.bridge_assoc.keys());
+ println!("{:?}", assoc.bridge_assoc.values());
- // spawn relay consumer
+ // ======================================================================================
+ // Relay consumer thread spawn
- shared_buffer_reference,
- shared_notify,
+ buffer_reference,
+ notify,
- irc_client.sender(),
+ irc_sender,
+ assoc
-
- while let Ok(Some(message)) = irc_stream.next().await.transpose() {
- let msg_clone = message.clone();
- match message.command {
- Command::PRIVMSG(_, contents) => {
- {
- let uname = msg_clone.source_nickname().unwrap();
- let mut buffer = buffer_reference.write().await;
- let mut new_message = RelayMessage::default();
- new_message.contents = contents;
- new_message.direction = RelayDirection::IRC2DIS;
- new_message.author.push_str(uname);
- buffer.pending_relay_messages.push_back(new_message);
- notify.notify.notify_one();
- }
- println!("Added IRC message to buffer");
- }
- _ => {}
- }
- }
}