Thumbnail

steew/belltoll.git

Clone URL: https://git.buni.party/steew/belltoll.git

Viewing file on branch master

1use std::collections::HashMap;
2use std::str::FromStr;
3use std::sync::Arc;
4
5use futures_util::StreamExt;
6use ini::Ini;
7use irc::client::data::Config;
8use irc::proto::Command;
9use serenity::all::ChannelId;
10use serenity::all::Settings;
11use serenity::all::Webhook;
12use serenity::prelude::*;
13use tokio::spawn;
14
15mod relay;
16mod relay_discord;
17mod relay_irc;
18
19use relay::*;
20use relay_discord::*;
21use relay_irc::*;
22
23#[tokio::main]
24async fn main() {
25 // Shared data initialization
26 // ======================================================================================
27 let buffer_reference = Arc::new(RwLock::new(MessageBuffer::default()));
28 let notify = Arc::new(RelayNotify::default());
29
30 let discord_notify = notify.clone();
31 let discord_buffer_reference = buffer_reference.clone();
32 let irc_notify = notify.clone();
33 let irc_buffer_reference = buffer_reference.clone();
34
35 // Discord initialization
36 // ======================================================================================
37 let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
38
39 let mut cache_settings = Settings::default();
40 cache_settings.max_messages = 10_000;
41
42 let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILDS | GatewayIntents::GUILD_MEMBERS;
43 let mut discord_client = serenity::Client::builder(&token, intents)
44 .cache_settings(cache_settings)
45 .event_handler(Handler)
46 .await
47 .expect("Err creating client");
48
49 // set the arbitrary data mutex as write
50 {
51 let mut data = discord_client.data.write().await;
52 data.insert::<MessageBuffer>(discord_buffer_reference);
53 data.insert::<RelayNotify>(discord_notify);
54 }
55
56 // shared http sender for the discord client
57 let shared_http = discord_client.http.clone();
58 let shared_cache = discord_client.cache.clone();
59
60 let avatars: HashMap<String, String> = HashMap::new();
61
62 // avatar url initialization
63
64 // spawn discord client async thread
65 let _client_handle = spawn(async move {
66 if let Err(why) = discord_client.start().await {
67 eprintln!("Client error: {why:?}");
68 }
69 });
70
71 // Configuration file read for channel bridge associations
72 // ======================================================================================
73 let ini_conf_file = "config.ini";
74 let ini = Ini::load_from_file(ini_conf_file).expect("A config.ini file is needed");
75
76 let mut assoc = RelayAssoc::default();
77
78 match ini.section(Some("assoc")) {
79 Some(section_contents) => {
80 for (d_channel, i_channel) in section_contents.iter() {
81 let chid = u64::from_str(d_channel).expect("Channel ID is not valid! {d_channel}");
82 let irc_chan = String::from_str(i_channel).expect("Channel name is not valid! {i_channel}");
83 let mut ircs = Vec::new();
84 ircs.push(irc_chan);
85 let chid = ChannelId::new(chid);
86 assoc.bridge_assoc.insert(chid, ircs.clone());
87 // =========================
88 }
89 },
90 None => { panic!("Expected an [assoc] section with bridge associations.") },
91 }
92
93 // find the webhook urls now
94 match ini.section(Some("webhook")) {
95 Some(section_contents) => {
96 for (chid_str, webhook_url) in section_contents.iter() {
97 let chid = u64::from_str(chid_str).expect("Channel ID is not valid! {chid_str}");
98 let webhook_url = String::from_str(webhook_url).expect("Webhook URL is not valid! {webhook_url}");
99 assoc.chid_webhook_assoc.insert(ChannelId::new(chid), webhook_url);
100 }
101 },
102 None => { panic!("Expected a [webhook] section with webhook associations.") },
103 }
104
105
106 println!("{:?}", assoc.bridge_assoc.keys());
107 println!("{:?}", assoc.bridge_assoc.values());
108 println!("Webhooks:");
109 println!("{:?}", assoc.chid_webhook_assoc.keys());
110 println!("Avatar URLs:");
111 println!("{:?}", avatars.values());
112
113 // ======================================================================================
114 // IRC initialization
115 let config = Config {
116 nickname: Some("belltoll".to_owned()),
117 server: Some("irc.libera.chat".to_owned()),
118 channels: vec!["##steew".to_owned()],
119 ..Default::default()
120 };
121
122 let irc_client = irc::client::Client::from_config(config).await.unwrap();
123 irc_client.identify().unwrap();
124 let irc_sender = irc_client.sender().clone();
125
126 for v in assoc.bridge_assoc.values() {
127 for c in v.iter() {
128 irc_client.send_join(c).expect("Could not join channel {c}");
129 }
130 }
131
132 let _irc_handle = spawn(async move {
133 irc_producer(irc_client, irc_buffer_reference, irc_notify).await;
134 });
135
136 // ======================================================================================
137 // Relay consumer thread spawn
138 let _relay_handle = spawn(async move {
139 relay_consumer(
140 buffer_reference,
141 notify,
142 shared_http,
143 irc_sender,
144 assoc,
145 avatars
146 )
147 .await;
148 });
149
150 loop {}
151}
152