Added context to systems
This commit is contained in:
parent
a1b1435b72
commit
789bfca745
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
@ -15,16 +15,20 @@ impl Irc {
|
|||||||
|
|
||||||
pub(crate) async fn event_welcome(&mut self, welcome_msg: &str) {
|
pub(crate) async fn event_welcome(&mut self, welcome_msg: &str) {
|
||||||
debug!("{welcome_msg}");
|
debug!("{welcome_msg}");
|
||||||
|
self.identify().await;
|
||||||
|
|
||||||
let mut context = self.context.write().await;
|
let mut context = self.context.write().await;
|
||||||
context.identify();
|
for channel in &self.config.channels {
|
||||||
context.join_config_channels();
|
context.join(channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn event_nicknameinuse(&mut self) {
|
pub(crate) async fn event_nicknameinuse(&mut self) {
|
||||||
let mut context = self.context.write().await;
|
let mut context = self.context.write().await;
|
||||||
let new_nick = &format!("{}_", &context.config.nick);
|
let new_nick = format!("{}_", &self.config.nick);
|
||||||
warn!("Nick already in use., switching to {}", new_nick);
|
warn!("Nick already in use., switching to {}", new_nick);
|
||||||
context.update_nick(new_nick)
|
context.nick(&new_nick);
|
||||||
|
self.config.nick = new_nick;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn event_kick(
|
pub(crate) async fn event_kick(
|
||||||
@ -35,7 +39,7 @@ impl Irc {
|
|||||||
reason: &str,
|
reason: &str,
|
||||||
) {
|
) {
|
||||||
let mut context = self.context.write().await;
|
let mut context = self.context.write().await;
|
||||||
if nick != &context.config.nick {
|
if nick != &self.config.nick {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +48,7 @@ impl Irc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn event_quit<'a>(&mut self, prefix: &'a IrcPrefix<'a>) {
|
pub(crate) async fn event_quit<'a>(&mut self, prefix: &'a IrcPrefix<'a>) {
|
||||||
if prefix.nick != self.context.read().await.config.nick {
|
if prefix.nick != self.config.nick {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +68,7 @@ impl Irc {
|
|||||||
channel: &str,
|
channel: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
) {
|
) {
|
||||||
let config = self.context.read().await.config.clone();
|
let config = self.config.clone();
|
||||||
|
|
||||||
if channel == &config.nick {
|
if channel == &config.nick {
|
||||||
if message.ends_with(&format!("\x02{}\x02 isn't registered.", config.nick)) {
|
if message.ends_with(&format!("\x02{}\x02 isn't registered.", config.nick)) {
|
||||||
@ -87,12 +91,13 @@ impl Irc {
|
|||||||
+ 1;
|
+ 1;
|
||||||
|
|
||||||
info!("Waiting {} seconds to register.", seconds);
|
info!("Waiting {} seconds to register.", seconds);
|
||||||
|
/* TODO: fix this
|
||||||
let ctx_clone = self.context.clone();
|
let ctx_clone = self.context.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tokio::time::sleep(Duration::from_secs(seconds as u64)).await;
|
tokio::time::sleep(Duration::from_secs(seconds as u64)).await;
|
||||||
ctx_clone.write().await.identify();
|
self.identify().await;
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,13 +112,13 @@ impl Irc {
|
|||||||
let sys_name;
|
let sys_name;
|
||||||
{
|
{
|
||||||
let context = self.context.read().await;
|
let context = self.context.read().await;
|
||||||
if !message.starts_with(&context.config.cmdkey) {
|
if !message.starts_with(&self.config.cmdkey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
elements = message.split_whitespace();
|
elements = message.split_whitespace();
|
||||||
sys_name = elements.next().unwrap()[1..].to_owned();
|
sys_name = elements.next().unwrap()[1..].to_owned();
|
||||||
|
|
||||||
if context.is_owner(prefix) && sys_name == "raw" {
|
if prefix.owner() && sys_name == "raw" {
|
||||||
drop(context);
|
drop(context);
|
||||||
let mut context = self.context.write().await;
|
let mut context = self.context.write().await;
|
||||||
context.queue(&elements.collect::<Vec<_>>().join(" "));
|
context.queue(&elements.collect::<Vec<_>>().join(" "));
|
||||||
@ -127,14 +132,14 @@ impl Irc {
|
|||||||
|
|
||||||
let arguments = elements.collect::<Vec<_>>();
|
let arguments = elements.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut context = self.context.write().await;
|
if !self.systems.contains_key(&sys_name) {
|
||||||
if !context.systems.contains_key(&sys_name) {
|
let resp = self.run_default_system(prefix, channel, &arguments).await;
|
||||||
let resp = context.run_default_system(prefix, &arguments).await;
|
|
||||||
|
|
||||||
let Response::Data(data) = resp else {
|
let Response::Data(data) = resp else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut context = self.context.write().await;
|
||||||
for (idx, line) in data.data.iter().enumerate() {
|
for (idx, line) in data.data.iter().enumerate() {
|
||||||
if idx == 0 && data.highlight {
|
if idx == 0 && data.highlight {
|
||||||
context.privmsg(channel, &format!("{}: {}", prefix.nick, line))
|
context.privmsg(channel, &format!("{}: {}", prefix.nick, line))
|
||||||
@ -145,11 +150,12 @@ impl Irc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = context.run_system(prefix, &arguments, &sys_name).await;
|
let response = self.run_system(prefix, channel, &arguments, &sys_name).await;
|
||||||
let Response::Data(data) = response else {
|
let Response::Data(data) = response else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut context = self.context.write().await;
|
||||||
for (idx, line) in data.data.iter().enumerate() {
|
for (idx, line) in data.data.iter().enumerate() {
|
||||||
if idx == 0 && data.highlight {
|
if idx == 0 && data.highlight {
|
||||||
context.privmsg(channel, &format!("{}: {}", prefix.nick, line))
|
context.privmsg(channel, &format!("{}: {}", prefix.nick, line))
|
||||||
|
421
src/lib.rs
421
src/lib.rs
@ -26,7 +26,7 @@ use tokio::{
|
|||||||
fs::File,
|
fs::File,
|
||||||
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadHalf, WriteHalf},
|
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadHalf, WriteHalf},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
sync::{mpsc, RwLock},
|
sync::RwLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) const MAX_MSG_LEN: usize = 512;
|
pub(crate) const MAX_MSG_LEN: usize = 512;
|
||||||
@ -69,11 +69,30 @@ impl Default for FloodControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub enum IrcPrefixKind {
|
||||||
|
Owner,
|
||||||
|
Admin,
|
||||||
|
#[default]
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct IrcPrefix<'a> {
|
pub struct IrcPrefix<'a> {
|
||||||
pub nick: &'a str,
|
pub nick: &'a str,
|
||||||
pub user: Option<&'a str>,
|
pub user: Option<&'a str>,
|
||||||
pub host: Option<&'a str>,
|
pub host: Option<&'a str>,
|
||||||
|
kind: IrcPrefixKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IrcPrefix<'a> {
|
||||||
|
pub fn owner(&self) -> bool {
|
||||||
|
self.kind == IrcPrefixKind::Owner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn admin(&self) -> bool {
|
||||||
|
self.kind == IrcPrefixKind::Admin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for IrcPrefix<'a> {
|
impl<'a> From<&'a str> for IrcPrefix<'a> {
|
||||||
@ -107,6 +126,7 @@ impl<'a> From<&'a str> for IrcPrefix<'a> {
|
|||||||
nick: nick,
|
nick: nick,
|
||||||
user: Some(user),
|
user: Some(user),
|
||||||
host: Some(user_split[1]),
|
host: Some(user_split[1]),
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,19 +183,11 @@ pub struct IrcConfig {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub struct Context {
|
pub struct IrcContext {
|
||||||
config: IrcConfig,
|
|
||||||
identified: bool,
|
|
||||||
send_queue: VecDeque<String>,
|
send_queue: VecDeque<String>,
|
||||||
|
|
||||||
default_system: Option<StoredSystem>,
|
|
||||||
invalid_system: Option<StoredSystem>,
|
|
||||||
systems: HashMap<String, StoredSystem>,
|
|
||||||
tasks: Vec<(Duration, StoredSystem)>,
|
|
||||||
factory: Arc<RwLock<Factory>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl IrcContext {
|
||||||
pub fn privmsg(&mut self, channel: &str, message: &str) {
|
pub fn privmsg(&mut self, channel: &str, message: &str) {
|
||||||
debug!("sending privmsg to {} : {}", channel, message);
|
debug!("sending privmsg to {} : {}", channel, message);
|
||||||
self.queue(&format!("PRIVMSG {} :{}", channel, message));
|
self.queue(&format!("PRIVMSG {} :{}", channel, message));
|
||||||
@ -200,136 +212,13 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identify(&mut self) {
|
pub fn join(&mut self, channel: &str) {
|
||||||
if self.config.nickserv_pass.is_none() || self.identified {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.privmsg(
|
|
||||||
"NickServ",
|
|
||||||
&format!("IDENTIFY {}", self.config.nickserv_pass.as_ref().unwrap()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register(&mut self) {
|
|
||||||
info!(
|
|
||||||
"Registering as {}!{} ({})",
|
|
||||||
self.config.nick, self.config.user, self.config.real
|
|
||||||
);
|
|
||||||
self.queue(&format!(
|
|
||||||
"USER {} 0 * {}",
|
|
||||||
self.config.user, self.config.real
|
|
||||||
));
|
|
||||||
self.queue(&format!("NICK {}", self.config.nick));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_owner(&self, prefix: &IrcPrefix) -> bool {
|
|
||||||
self.is_admin(prefix, &self.config.owner)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_admin(&self, prefix: &IrcPrefix, admin: &str) -> bool {
|
|
||||||
let admin = ":".to_owned() + &admin;
|
|
||||||
let admin_prefix: IrcPrefix = admin.as_str().into();
|
|
||||||
|
|
||||||
if (admin_prefix.nick == prefix.nick || admin_prefix.nick == "*")
|
|
||||||
&& (admin_prefix.user == prefix.user || admin_prefix.user == Some("*"))
|
|
||||||
&& (admin_prefix.host == prefix.host || admin_prefix.host == Some("*"))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn join(&mut self, channel: &str) {
|
|
||||||
info!("Joining {channel}");
|
info!("Joining {channel}");
|
||||||
self.queue(&format!("JOIN {}", channel));
|
self.queue(&format!("JOIN {}", channel));
|
||||||
self.config.channels.insert(channel.to_owned());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join_config_channels(&mut self) {
|
pub fn nick(&mut self, nick: &str) {
|
||||||
for i in 0..self.config.channels.len() {
|
self.queue(&format!("NICK {}", nick));
|
||||||
let channel = self.config.channels.iter().nth(i).unwrap();
|
|
||||||
info!("Joining {channel}");
|
|
||||||
self.queue(&format!("JOIN {}", channel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_nick(&mut self, new_nick: &str) {
|
|
||||||
self.config.nick = new_nick.to_owned();
|
|
||||||
self.queue(&format!("NICK {}", self.config.nick));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn privmsg_all(&mut self, message: &str) {
|
|
||||||
for i in 0..self.config.channels.len() {
|
|
||||||
let channel = self.config.channels.iter().nth(i).unwrap();
|
|
||||||
debug!("sending privmsg to {} : {}", channel, message);
|
|
||||||
self.queue(&format!("PRIVMSG {} :{}", channel, message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_system<'a>(
|
|
||||||
&mut self,
|
|
||||||
prefix: &'a IrcPrefix<'a>,
|
|
||||||
arguments: &'a [&'a str],
|
|
||||||
name: &str,
|
|
||||||
) -> Response {
|
|
||||||
let system = self.systems.get_mut(name).unwrap();
|
|
||||||
system.run(prefix, arguments, &mut *self.factory.write().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_default_system<'a>(
|
|
||||||
&mut self,
|
|
||||||
prefix: &'a IrcPrefix<'a>,
|
|
||||||
arguments: &'a [&'a str],
|
|
||||||
) -> Response {
|
|
||||||
if self.invalid_system.is_none() {
|
|
||||||
return Response::Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.default_system.as_mut().unwrap().run(
|
|
||||||
prefix,
|
|
||||||
arguments,
|
|
||||||
&mut *self.factory.write().await,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_invalid_system<'a>(
|
|
||||||
&mut self,
|
|
||||||
prefix: &'a IrcPrefix<'a>,
|
|
||||||
arguments: &'a [&'a str],
|
|
||||||
) -> Response {
|
|
||||||
if self.invalid_system.is_none() {
|
|
||||||
return Response::Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.invalid_system.as_mut().unwrap().run(
|
|
||||||
prefix,
|
|
||||||
arguments,
|
|
||||||
&mut *self.factory.write().await,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_interval_tasks(&mut self, tx: mpsc::Sender<Response>) {
|
|
||||||
for (duration, mut task) in std::mem::take(&mut self.tasks) {
|
|
||||||
let fact = self.factory.clone();
|
|
||||||
let task_tx = tx.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
loop {
|
|
||||||
tokio::time::sleep(duration).await;
|
|
||||||
let resp = task.run(
|
|
||||||
&IrcPrefix {
|
|
||||||
nick: "",
|
|
||||||
user: None,
|
|
||||||
host: None,
|
|
||||||
},
|
|
||||||
&[],
|
|
||||||
&mut *fact.write().await,
|
|
||||||
);
|
|
||||||
task_tx.send(resp).await.unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,10 +227,19 @@ pub trait AsyncReadWrite: AsyncRead + AsyncWrite + Send + Unpin {}
|
|||||||
impl<T: AsyncRead + AsyncWrite + Send + Unpin> AsyncReadWrite for T {}
|
impl<T: AsyncRead + AsyncWrite + Send + Unpin> AsyncReadWrite for T {}
|
||||||
|
|
||||||
pub struct Irc {
|
pub struct Irc {
|
||||||
context: Arc<RwLock<Context>>,
|
context: Arc<RwLock<IrcContext>>,
|
||||||
flood_controls: HashMap<String, FloodControl>,
|
flood_controls: HashMap<String, FloodControl>,
|
||||||
stream: Option<Box<dyn AsyncReadWrite>>,
|
stream: Option<Box<dyn AsyncReadWrite>>,
|
||||||
partial_line: String,
|
partial_line: String,
|
||||||
|
|
||||||
|
config: IrcConfig,
|
||||||
|
identified: bool,
|
||||||
|
|
||||||
|
default_system: Option<StoredSystem>,
|
||||||
|
invalid_system: Option<StoredSystem>,
|
||||||
|
systems: HashMap<String, StoredSystem>,
|
||||||
|
tasks: Vec<(Duration, StoredSystem)>,
|
||||||
|
factory: Arc<RwLock<Factory>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Irc {
|
impl Irc {
|
||||||
@ -352,15 +250,8 @@ impl Irc {
|
|||||||
|
|
||||||
let config: IrcConfig = serde_yaml::from_str(&contents).unwrap();
|
let config: IrcConfig = serde_yaml::from_str(&contents).unwrap();
|
||||||
|
|
||||||
let context = Arc::new(RwLock::new(Context {
|
let context = Arc::new(RwLock::new(IrcContext {
|
||||||
config,
|
|
||||||
identified: false,
|
|
||||||
send_queue: VecDeque::new(),
|
send_queue: VecDeque::new(),
|
||||||
default_system: None,
|
|
||||||
invalid_system: None,
|
|
||||||
systems: HashMap::default(),
|
|
||||||
tasks: Vec::new(),
|
|
||||||
factory: Arc::new(RwLock::new(Factory::default())),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -368,6 +259,13 @@ impl Irc {
|
|||||||
stream: None,
|
stream: None,
|
||||||
flood_controls: HashMap::default(),
|
flood_controls: HashMap::default(),
|
||||||
partial_line: String::new(),
|
partial_line: String::new(),
|
||||||
|
config,
|
||||||
|
identified: false,
|
||||||
|
default_system: None,
|
||||||
|
invalid_system: None,
|
||||||
|
systems: HashMap::default(),
|
||||||
|
tasks: Vec::new(),
|
||||||
|
factory: Arc::new(RwLock::new(Factory::default())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,12 +274,8 @@ impl Irc {
|
|||||||
name: &str,
|
name: &str,
|
||||||
system: impl for<'a> IntoSystem<I, System = S>,
|
system: impl for<'a> IntoSystem<I, System = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
{
|
self.systems
|
||||||
let mut context = self.context.write().await;
|
.insert(name.to_owned(), Box::new(system.into_system()));
|
||||||
context
|
|
||||||
.systems
|
|
||||||
.insert(name.to_owned(), Box::new(system.into_system()));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,10 +283,7 @@ impl Irc {
|
|||||||
&mut self,
|
&mut self,
|
||||||
system: impl for<'a> IntoSystem<I, System = S>,
|
system: impl for<'a> IntoSystem<I, System = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
{
|
self.default_system = Some(Box::new(system.into_system()));
|
||||||
let mut context = self.context.write().await;
|
|
||||||
context.default_system = Some(Box::new(system.into_system()));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,10 +291,7 @@ impl Irc {
|
|||||||
&mut self,
|
&mut self,
|
||||||
system: impl for<'a> IntoSystem<I, System = S>,
|
system: impl for<'a> IntoSystem<I, System = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
{
|
self.invalid_system = Some(Box::new(system.into_system()));
|
||||||
let mut context = self.context.write().await;
|
|
||||||
context.invalid_system = Some(Box::new(system.into_system()));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,10 +300,7 @@ impl Irc {
|
|||||||
duration: Duration,
|
duration: Duration,
|
||||||
system: impl for<'a> IntoSystem<I, System = S>,
|
system: impl for<'a> IntoSystem<I, System = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
{
|
self.tasks.push((duration, Box::new(system.into_system())));
|
||||||
let mut context = self.context.write().await;
|
|
||||||
context.tasks.push((duration, Box::new(system.into_system())));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,30 +308,22 @@ impl Irc {
|
|||||||
&mut self,
|
&mut self,
|
||||||
system: impl for<'a> IntoSystem<I, System = S>,
|
system: impl for<'a> IntoSystem<I, System = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
{
|
self.tasks
|
||||||
let mut context = self.context.write().await;
|
.push((Duration::ZERO, Box::new(system.into_system())));
|
||||||
context.tasks.push((Duration::ZERO, Box::new(system.into_system())));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_resource<R: Send + Sync + 'static>(&mut self, res: R) -> &mut Self {
|
pub async fn add_resource<R: Send + Sync + 'static>(&mut self, res: R) -> &mut Self {
|
||||||
{
|
self.factory
|
||||||
let context = self.context.write().await;
|
.write()
|
||||||
context
|
.await
|
||||||
.factory
|
.resources
|
||||||
.write()
|
.insert(TypeId::of::<R>(), Box::new(res));
|
||||||
.await
|
|
||||||
.resources
|
|
||||||
.insert(TypeId::of::<R>(), Box::new(res));
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect(&mut self) -> std::io::Result<()> {
|
pub async fn connect(&mut self) -> std::io::Result<()> {
|
||||||
let context = self.context.read().await;
|
let domain = format!("{}:{}", self.config.host, self.config.port);
|
||||||
|
|
||||||
let domain = format!("{}:{}", context.config.host, context.config.port);
|
|
||||||
|
|
||||||
info!("Connecting to {}", domain);
|
info!("Connecting to {}", domain);
|
||||||
|
|
||||||
@ -460,8 +337,8 @@ impl Irc {
|
|||||||
|
|
||||||
let plain_stream = TcpStream::connect(sock).await?;
|
let plain_stream = TcpStream::connect(sock).await?;
|
||||||
|
|
||||||
if context.config.ssl {
|
if self.config.ssl {
|
||||||
let stream = async_native_tls::connect(context.config.host.clone(), plain_stream)
|
let stream = async_native_tls::connect(self.config.host.clone(), plain_stream)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.stream = Some(Box::new(stream));
|
self.stream = Some(Box::new(stream));
|
||||||
@ -485,7 +362,7 @@ impl Irc {
|
|||||||
|
|
||||||
let elapsed = flood_control.last_cmd.elapsed().unwrap();
|
let elapsed = flood_control.last_cmd.elapsed().unwrap();
|
||||||
|
|
||||||
if elapsed.as_secs_f32() < self.context.read().await.config.flood_interval {
|
if elapsed.as_secs_f32() < self.config.flood_interval {
|
||||||
warn!("they be floodin @ {channel}!");
|
warn!("they be floodin @ {channel}!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -494,6 +371,50 @@ impl Irc {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_owner(&self, prefix: &IrcPrefix) -> bool {
|
||||||
|
let owner = ":".to_owned() + &self.config.owner;
|
||||||
|
let owner_prefix: IrcPrefix = owner.as_str().into();
|
||||||
|
|
||||||
|
if (owner_prefix.nick == prefix.nick || owner_prefix.nick == "*")
|
||||||
|
&& (owner_prefix.user == prefix.user || owner_prefix.user == Some("*"))
|
||||||
|
&& (owner_prefix.host == prefix.host || owner_prefix.host == Some("*"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_admin(&self, prefix: &IrcPrefix) -> bool {
|
||||||
|
for admin_str in &self.config.admins {
|
||||||
|
let admin = ":".to_owned() + admin_str;
|
||||||
|
let admin_prefix: IrcPrefix = admin.as_str().into();
|
||||||
|
|
||||||
|
if (admin_prefix.nick == prefix.nick || admin_prefix.nick == "*")
|
||||||
|
&& (admin_prefix.user == prefix.user || admin_prefix.user == Some("*"))
|
||||||
|
&& (admin_prefix.host == prefix.host || admin_prefix.host == Some("*"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_message<'a>(&self, line: &'a str) -> IrcMessage<'a> {
|
||||||
|
let mut message: IrcMessage = line.into();
|
||||||
|
|
||||||
|
if let Some(prefix) = &mut message.prefix {
|
||||||
|
if self.is_owner(prefix) {
|
||||||
|
prefix.kind = IrcPrefixKind::Owner;
|
||||||
|
} else if self.is_admin(prefix) {
|
||||||
|
prefix.kind = IrcPrefixKind::Admin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_commands(&mut self, mut lines: VecDeque<String>) {
|
pub async fn handle_commands(&mut self, mut lines: VecDeque<String>) {
|
||||||
while lines.len() != 0 {
|
while lines.len() != 0 {
|
||||||
let owned_line = lines.pop_front().unwrap();
|
let owned_line = lines.pop_front().unwrap();
|
||||||
@ -501,7 +422,8 @@ impl Irc {
|
|||||||
|
|
||||||
trace!("<< {:?}", line);
|
trace!("<< {:?}", line);
|
||||||
|
|
||||||
let message: IrcMessage = line.into();
|
let message = self.into_message(line);
|
||||||
|
|
||||||
self.handle_message(&message).await;
|
self.handle_message(&message).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -548,27 +470,116 @@ impl Irc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn register(&mut self) {
|
||||||
|
info!(
|
||||||
|
"Registering as {}!{} ({})",
|
||||||
|
self.config.nick, self.config.user, self.config.real
|
||||||
|
);
|
||||||
|
let mut context = self.context.write().await;
|
||||||
|
|
||||||
|
context.queue(&format!(
|
||||||
|
"USER {} 0 * {}",
|
||||||
|
self.config.user, self.config.real
|
||||||
|
));
|
||||||
|
context.nick(&self.config.nick);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn identify(&mut self) {
|
||||||
|
if self.config.nickserv_pass.is_none() || self.identified {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.context.write().await.privmsg(
|
||||||
|
"NickServ",
|
||||||
|
&format!("IDENTIFY {}", self.config.nickserv_pass.as_ref().unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_system<'a>(
|
||||||
|
&mut self,
|
||||||
|
prefix: &'a IrcPrefix<'a>,
|
||||||
|
channel: &'a str,
|
||||||
|
arguments: &'a [&'a str],
|
||||||
|
name: &str,
|
||||||
|
) -> Response {
|
||||||
|
let system = self.systems.get_mut(name).unwrap();
|
||||||
|
system.run(
|
||||||
|
prefix,
|
||||||
|
channel,
|
||||||
|
arguments,
|
||||||
|
&mut *self.context.write().await,
|
||||||
|
&mut *self.factory.write().await,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_default_system<'a>(
|
||||||
|
&mut self,
|
||||||
|
prefix: &'a IrcPrefix<'a>,
|
||||||
|
channel: &'a str,
|
||||||
|
arguments: &'a [&'a str],
|
||||||
|
) -> Response {
|
||||||
|
if self.invalid_system.is_none() {
|
||||||
|
return Response::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.default_system.as_mut().unwrap().run(
|
||||||
|
prefix,
|
||||||
|
channel,
|
||||||
|
arguments,
|
||||||
|
&mut *self.context.write().await,
|
||||||
|
&mut *self.factory.write().await,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_invalid_system<'a>(
|
||||||
|
&mut self,
|
||||||
|
prefix: &'a IrcPrefix<'a>,
|
||||||
|
channel: &'a str,
|
||||||
|
arguments: &'a [&'a str],
|
||||||
|
) -> Response {
|
||||||
|
if self.invalid_system.is_none() {
|
||||||
|
return Response::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.invalid_system.as_mut().unwrap().run(
|
||||||
|
prefix,
|
||||||
|
channel,
|
||||||
|
arguments,
|
||||||
|
&mut *self.context.write().await,
|
||||||
|
&mut *self.factory.write().await,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_interval_tasks(&mut self) {
|
||||||
|
for (duration, mut task) in std::mem::take(&mut self.tasks) {
|
||||||
|
let fact = self.factory.clone();
|
||||||
|
let ctx = self.context.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
task.run(
|
||||||
|
&IrcPrefix::default(),
|
||||||
|
"",
|
||||||
|
&[],
|
||||||
|
&mut *ctx.write().await,
|
||||||
|
&mut *fact.write().await,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> std::io::Result<()> {
|
pub async fn run(&mut self) -> std::io::Result<()> {
|
||||||
self.connect().await?;
|
self.connect().await?;
|
||||||
info!("Ready!");
|
info!("Ready!");
|
||||||
let (tx, mut rx) = mpsc::channel::<Response>(512);
|
self.register().await;
|
||||||
{
|
|
||||||
let mut context = self.context.write().await;
|
self.run_interval_tasks().await;
|
||||||
context.register();
|
|
||||||
context.run_interval_tasks(tx).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stream = self.stream.take().unwrap();
|
let stream = self.stream.take().unwrap();
|
||||||
|
|
||||||
let (mut reader, mut writer) = tokio::io::split(stream);
|
let (mut reader, mut writer) = tokio::io::split(stream);
|
||||||
|
|
||||||
let cloned_ctx = self.context.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
loop {
|
|
||||||
handle_rx(&mut rx, &cloned_ctx).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let cloned_ctx = self.context.clone();
|
let cloned_ctx = self.context.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
@ -583,23 +594,9 @@ impl Irc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_rx(rx: &mut mpsc::Receiver<Response>, arc_context: &RwLock<Context>) {
|
|
||||||
while let Some(response) = rx.recv().await {
|
|
||||||
let mut context = arc_context.write().await;
|
|
||||||
|
|
||||||
let Response::Data(data) = response else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
for line in data.data {
|
|
||||||
context.privmsg_all(&line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send<T: AsyncWrite>(
|
async fn send<T: AsyncWrite>(
|
||||||
writer: &mut WriteHalf<T>,
|
writer: &mut WriteHalf<T>,
|
||||||
arc_context: &RwLock<Context>,
|
arc_context: &RwLock<IrcContext>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let mut len;
|
let mut len;
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{factory::Factory, format::Msg, IrcPrefix};
|
use crate::{factory::Factory, format::Msg, IrcContext, IrcPrefix};
|
||||||
|
|
||||||
pub struct FunctionSystem<Input, F> {
|
pub struct FunctionSystem<Input, F> {
|
||||||
f: F,
|
f: F,
|
||||||
@ -8,7 +8,14 @@ pub struct FunctionSystem<Input, F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait System {
|
pub trait System {
|
||||||
fn run(&mut self, prefix: &IrcPrefix, arguments: &[&str], factory: &mut Factory) -> Response;
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
prefix: &IrcPrefix,
|
||||||
|
channel: &str,
|
||||||
|
arguments: &[&str],
|
||||||
|
context: &mut IrcContext,
|
||||||
|
factory: &mut Factory,
|
||||||
|
) -> Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoSystem<Input> {
|
pub trait IntoSystem<Input> {
|
||||||
@ -29,7 +36,7 @@ macro_rules! impl_system {
|
|||||||
FnMut( $($params),* ) -> R +
|
FnMut( $($params),* ) -> R +
|
||||||
FnMut( $(<$params as SystemParam>::Item<'b>),* ) -> R
|
FnMut( $(<$params as SystemParam>::Item<'b>),* ) -> R
|
||||||
{
|
{
|
||||||
fn run(&mut self, prefix: &IrcPrefix, arguments: &[&str], factory: &mut Factory) -> Response {
|
fn run(&mut self, prefix: &IrcPrefix, channel: &str, arguments: &[&str], context: &mut IrcContext, factory: &mut Factory) -> Response {
|
||||||
fn call_inner<'a, R: IntoResponse, $($params),*>(
|
fn call_inner<'a, R: IntoResponse, $($params),*>(
|
||||||
mut f: impl FnMut($($params),*) -> R,
|
mut f: impl FnMut($($params),*) -> R,
|
||||||
$($params: $params),*
|
$($params: $params),*
|
||||||
@ -42,7 +49,7 @@ macro_rules! impl_system {
|
|||||||
return Response::InvalidArgument;
|
return Response::InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
let $params = $params::retrieve(prefix, arguments, &factory);
|
let $params = $params::retrieve(prefix, channel, arguments, &context, &factory);
|
||||||
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
@ -116,7 +123,9 @@ pub(crate) trait SystemParam {
|
|||||||
type Item<'new>;
|
type Item<'new>;
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
prefix: &'r IrcPrefix,
|
prefix: &'r IrcPrefix,
|
||||||
|
channel: &'r str,
|
||||||
arguments: &'r [&'r str],
|
arguments: &'r [&'r str],
|
||||||
|
context: &'r IrcContext,
|
||||||
factory: &'r Factory,
|
factory: &'r Factory,
|
||||||
) -> Self::Item<'r>;
|
) -> Self::Item<'r>;
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
@ -4,7 +4,7 @@ use std::{
|
|||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{factory::Factory, system::SystemParam, IrcPrefix};
|
use crate::{factory::Factory, system::SystemParam, IrcContext, IrcPrefix};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Res<'a, T: 'static> {
|
pub struct Res<'a, T: 'static> {
|
||||||
@ -30,7 +30,9 @@ impl<'res, T: 'static> SystemParam for Res<'res, T> {
|
|||||||
|
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
_prefix: &'r IrcPrefix,
|
_prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
_arguments: &'r [&'r str],
|
_arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
factory: &'r Factory,
|
factory: &'r Factory,
|
||||||
) -> Self::Item<'r> {
|
) -> Self::Item<'r> {
|
||||||
Res {
|
Res {
|
||||||
@ -79,7 +81,9 @@ impl<'res, T: 'static> SystemParam for ResMut<'res, T> {
|
|||||||
|
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
_prefix: &'r IrcPrefix,
|
_prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
_arguments: &'r [&'r str],
|
_arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
factory: &'r Factory,
|
factory: &'r Factory,
|
||||||
) -> Self::Item<'r> {
|
) -> Self::Item<'r> {
|
||||||
let const_ptr = &factory.resources as *const HashMap<TypeId, Box<dyn Any + Send + Sync>>;
|
let const_ptr = &factory.resources as *const HashMap<TypeId, Box<dyn Any + Send + Sync>>;
|
||||||
@ -101,13 +105,38 @@ impl<'a> SystemParam for IrcPrefix<'a> {
|
|||||||
|
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
prefix: &'r IrcPrefix,
|
prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
_arguments: &'r [&'r str],
|
_arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
_factory: &'r Factory,
|
_factory: &'r Factory,
|
||||||
) -> Self::Item<'r> {
|
) -> Self::Item<'r> {
|
||||||
prefix.clone()
|
prefix.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Channel<'a>(&'a str);
|
||||||
|
impl<'a> Deref for Channel<'a> {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SystemParam for Channel<'a> {
|
||||||
|
type Item<'new> = Channel<'new>;
|
||||||
|
|
||||||
|
fn retrieve<'r>(
|
||||||
|
_prefix: &'r IrcPrefix,
|
||||||
|
channel: &'r str,
|
||||||
|
_arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
|
_factory: &'r Factory,
|
||||||
|
) -> Self::Item<'r> {
|
||||||
|
Channel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AnyArguments<'a>(&'a [&'a str]);
|
pub struct AnyArguments<'a>(&'a [&'a str]);
|
||||||
|
|
||||||
impl<'a> Deref for AnyArguments<'a> {
|
impl<'a> Deref for AnyArguments<'a> {
|
||||||
@ -123,7 +152,9 @@ impl<'a> SystemParam for AnyArguments<'a> {
|
|||||||
|
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
_prefix: &'r IrcPrefix,
|
_prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
arguments: &'r [&'r str],
|
arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
_factory: &'r Factory,
|
_factory: &'r Factory,
|
||||||
) -> Self::Item<'r> {
|
) -> Self::Item<'r> {
|
||||||
AnyArguments(&arguments)
|
AnyArguments(&arguments)
|
||||||
@ -145,7 +176,9 @@ impl<'a, const N: usize> SystemParam for Arguments<'a, N> {
|
|||||||
|
|
||||||
fn retrieve<'r>(
|
fn retrieve<'r>(
|
||||||
_prefix: &'r IrcPrefix,
|
_prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
arguments: &'r [&'r str],
|
arguments: &'r [&'r str],
|
||||||
|
_context: &'r IrcContext,
|
||||||
_factory: &'r Factory,
|
_factory: &'r Factory,
|
||||||
) -> Self::Item<'r> {
|
) -> Self::Item<'r> {
|
||||||
Arguments(&arguments[..N])
|
Arguments(&arguments[..N])
|
||||||
@ -155,3 +188,36 @@ impl<'a, const N: usize> SystemParam for Arguments<'a, N> {
|
|||||||
arguments.len() == N
|
arguments.len() == N
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Context<'a>(&'a mut IrcContext);
|
||||||
|
|
||||||
|
impl<'a> Deref for Context<'a> {
|
||||||
|
type Target = IrcContext;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for Context<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SystemParam for Context<'a> {
|
||||||
|
type Item<'new> = Context<'new>;
|
||||||
|
|
||||||
|
fn retrieve<'r>(
|
||||||
|
_prefix: &'r IrcPrefix,
|
||||||
|
_channel: &str,
|
||||||
|
_arguments: &'r [&'r str],
|
||||||
|
context: &'r IrcContext,
|
||||||
|
_factory: &'r Factory,
|
||||||
|
) -> Self::Item<'r> {
|
||||||
|
let const_ptr = context as *const IrcContext;
|
||||||
|
let mut_ptr = const_ptr as *mut IrcContext;
|
||||||
|
let ctx_mut = unsafe { &mut *mut_ptr };
|
||||||
|
Context(ctx_mut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user