Compare commits
2 commits
7f518f7e1e
...
6209393cd0
Author | SHA1 | Date | |
---|---|---|---|
6209393cd0 | |||
c6f13f9388 |
3 changed files with 127 additions and 66 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
config.toml
|
35
src/irc_handler.rs
Normal file
35
src/irc_handler.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use tokio::{
|
||||||
|
select,
|
||||||
|
sync::mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn connect() -> Result<irc::client::Client> {
|
||||||
|
Ok(irc::client::Client::new("config.toml").await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn message_loop(
|
||||||
|
mut client: irc::client::Client,
|
||||||
|
message_tx: UnboundedSender<irc::proto::Message>,
|
||||||
|
mut input_rx: UnboundedReceiver<irc::proto::Message>,
|
||||||
|
) -> Result<()> {
|
||||||
|
client.identify()?;
|
||||||
|
let mut irc_stream = client.stream()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
val = irc_stream.next() => {
|
||||||
|
if let Some(message) = val.transpose()? {
|
||||||
|
print!("[Rx] {message}");
|
||||||
|
message_tx.send(message)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val = input_rx.recv() => {
|
||||||
|
let message = val.unwrap();
|
||||||
|
print!("[Tx] {message}");
|
||||||
|
client.send(message)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
src/main.rs
157
src/main.rs
|
@ -1,9 +1,9 @@
|
||||||
|
mod irc_handler;
|
||||||
mod irc_message;
|
mod irc_message;
|
||||||
mod message_log;
|
mod message_log;
|
||||||
|
|
||||||
use crate::{irc_message::IrcMessage, message_log::MessageLog};
|
use crate::{irc_message::IrcMessage, message_log::MessageLog};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use futures::StreamExt;
|
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment::{Horizontal, Vertical},
|
alignment::{Horizontal, Vertical},
|
||||||
executor,
|
executor,
|
||||||
|
@ -11,40 +11,13 @@ use iced::{
|
||||||
widget::{column, container, mouse_area, row, text, text_input},
|
widget::{column, container, mouse_area, row, text, text_input},
|
||||||
Application, Background, Color, Element, Length, Settings,
|
Application, Background, Color, Element, Length, Settings,
|
||||||
};
|
};
|
||||||
|
use irc::proto::Command as IrcCommand;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use tokio::{
|
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
select,
|
|
||||||
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
|
|
||||||
};
|
|
||||||
|
|
||||||
static INPUT_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique);
|
static INPUT_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique);
|
||||||
|
|
||||||
async fn irc_loop(
|
|
||||||
mut client: irc::client::Client,
|
|
||||||
message_tx: UnboundedSender<irc::proto::Message>,
|
|
||||||
mut input_rx: UnboundedReceiver<irc::proto::Message>,
|
|
||||||
) -> Result<()> {
|
|
||||||
client.identify()?;
|
|
||||||
let mut irc_stream = client.stream()?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
select! {
|
|
||||||
val = irc_stream.next() => {
|
|
||||||
if let Some(message) = val.transpose()? {
|
|
||||||
print!("[Rx] {message}");
|
|
||||||
message_tx.send(message)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val = input_rx.recv() => {
|
|
||||||
let message = val.unwrap();
|
|
||||||
print!("[Tx] {message}");
|
|
||||||
client.send(message)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
@ -52,20 +25,8 @@ async fn main() -> Result<()> {
|
||||||
let (message_tx, message_rx) = mpsc::unbounded_channel::<irc::proto::Message>();
|
let (message_tx, message_rx) = mpsc::unbounded_channel::<irc::proto::Message>();
|
||||||
let (input_tx, input_rx) = mpsc::unbounded_channel::<irc::proto::Message>();
|
let (input_tx, input_rx) = mpsc::unbounded_channel::<irc::proto::Message>();
|
||||||
|
|
||||||
let config = irc::client::prelude::Config {
|
let client = irc_handler::connect().await?;
|
||||||
nickname: Some("cri".to_string()),
|
let task = tokio::task::spawn(irc_handler::message_loop(client, message_tx, input_rx));
|
||||||
server: Some("vijf.life".to_string()),
|
|
||||||
channels: vec![
|
|
||||||
"#h".to_string(),
|
|
||||||
"#test".to_string(),
|
|
||||||
"#test1".to_string(),
|
|
||||||
"#test2".to_string(),
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = irc::client::Client::from_config(config).await?;
|
|
||||||
let task = tokio::task::spawn(irc_loop(client, message_tx, input_rx));
|
|
||||||
|
|
||||||
Cri::run(Settings::with_flags(CriFlags {
|
Cri::run(Settings::with_flags(CriFlags {
|
||||||
message_rx,
|
message_rx,
|
||||||
|
@ -97,6 +58,81 @@ struct Cri {
|
||||||
input_value: String,
|
input_value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Cri {
|
||||||
|
fn send_message(&mut self, input_value: &str) {
|
||||||
|
if let Some(active_channel) = &self.message_log.active_channel {
|
||||||
|
let command = IrcCommand::PRIVMSG(active_channel.to_string(), input_value.to_string());
|
||||||
|
let message: irc::proto::Message = command.into();
|
||||||
|
|
||||||
|
self.message_log
|
||||||
|
.get_mut(self.message_log.active_channel.clone())
|
||||||
|
.messages
|
||||||
|
.push(IrcMessage::Privmsg {
|
||||||
|
nickname: String::from("cri"),
|
||||||
|
message: input_value.to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.input_tx.borrow().send(message.clone()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_command(&mut self, command: &str) {
|
||||||
|
let mut tokens = command.split_whitespace();
|
||||||
|
|
||||||
|
let command = tokens.next().unwrap();
|
||||||
|
match command {
|
||||||
|
"/join" => {
|
||||||
|
let channel = tokens.next().map(str::to_string).or_else(|| self.message_log.active_channel.clone());
|
||||||
|
if channel.is_none() {
|
||||||
|
// TODO error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = channel.unwrap();
|
||||||
|
if !channel.starts_with('#') {
|
||||||
|
// TODO error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
.input_tx
|
||||||
|
.borrow()
|
||||||
|
.send(IrcCommand::JOIN(channel, tokens.next().map(str::to_string), None).into())
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
"/part" => {
|
||||||
|
let channel = tokens.next().map(str::to_string).or_else(|| self.message_log.active_channel.clone());
|
||||||
|
if channel.is_none() {
|
||||||
|
// TODO error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = channel.unwrap();
|
||||||
|
if !channel.starts_with('#') {
|
||||||
|
// TODO error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
.input_tx
|
||||||
|
.borrow()
|
||||||
|
.send(
|
||||||
|
IrcCommand::PART(
|
||||||
|
channel,
|
||||||
|
tokens.next().map(str::to_string),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
"/query" => self
|
||||||
|
.message_log
|
||||||
|
.set_active(Some(tokens.next().unwrap().to_string())),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Application for Cri {
|
impl Application for Cri {
|
||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
type Message = UiMessage;
|
type Message = UiMessage;
|
||||||
|
@ -120,33 +156,31 @@ impl Application for Cri {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
|
||||||
use irc::proto::Command;
|
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
UiMessage::IrcMessageReceived(message) => {
|
UiMessage::IrcMessageReceived(message) => {
|
||||||
// TODO use actual nickname
|
// TODO use actual nickname
|
||||||
let source_nickname = message.source_nickname().unwrap_or("cri").to_string();
|
let source_nickname = message.source_nickname().unwrap_or("cri").to_string();
|
||||||
|
|
||||||
match &message.command {
|
match &message.command {
|
||||||
Command::JOIN(chanlist, _, _) => {
|
IrcCommand::JOIN(chanlist, _, _) => {
|
||||||
self.message_log.on_join(chanlist, &source_nickname);
|
self.message_log.on_join(chanlist, &source_nickname);
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::PART(chanlist, comment) => {
|
IrcCommand::PART(chanlist, comment) => {
|
||||||
self.message_log
|
self.message_log
|
||||||
.on_part(chanlist, &source_nickname, comment.as_deref());
|
.on_part(chanlist, &source_nickname, comment.as_deref());
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::NICK(new) => {
|
IrcCommand::NICK(new) => {
|
||||||
self.message_log.on_nick(&source_nickname, new);
|
self.message_log.on_nick(&source_nickname, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::QUIT(comment) => {
|
IrcCommand::QUIT(comment) => {
|
||||||
self.message_log
|
self.message_log
|
||||||
.on_quit(&source_nickname, comment.as_deref());
|
.on_quit(&source_nickname, comment.as_deref());
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::PRIVMSG(msgtarget, content) => {
|
IrcCommand::PRIVMSG(msgtarget, content) => {
|
||||||
let channel = message.response_target().unwrap_or(msgtarget).to_string();
|
let channel = message.response_target().unwrap_or(msgtarget).to_string();
|
||||||
self.message_log
|
self.message_log
|
||||||
.on_privmsg(&channel, &source_nickname, content);
|
.on_privmsg(&channel, &source_nickname, content);
|
||||||
|
@ -157,21 +191,12 @@ impl Application for Cri {
|
||||||
}
|
}
|
||||||
UiMessage::InputChanged(text) => self.input_value = text,
|
UiMessage::InputChanged(text) => self.input_value = text,
|
||||||
UiMessage::InputSubmitted => {
|
UiMessage::InputSubmitted => {
|
||||||
if let Some(active_channel) = &self.message_log.active_channel {
|
if self.input_value.starts_with("//") {
|
||||||
let command = irc::proto::Command::PRIVMSG(
|
self.send_message(&self.input_value.clone()[1..])
|
||||||
active_channel.to_string(),
|
} else if self.input_value.starts_with('/') {
|
||||||
self.input_value.clone(),
|
self.send_command(&self.input_value.clone())
|
||||||
);
|
} else {
|
||||||
let message: irc::proto::Message = command.into();
|
self.send_message(&self.input_value.clone());
|
||||||
|
|
||||||
self.message_log
|
|
||||||
.get_mut(self.message_log.active_channel.clone())
|
|
||||||
.messages
|
|
||||||
.push(IrcMessage::Privmsg {
|
|
||||||
nickname: String::from("cri"),
|
|
||||||
message: self.input_value.clone(),
|
|
||||||
});
|
|
||||||
self.input_tx.borrow().send(message.clone()).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.input_value.clear();
|
self.input_value.clear();
|
||||||
|
|
Loading…
Reference in a new issue