1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use anyhow::{anyhow, Result};
use irc::client::data::AccessLevel;
use irc::client::prelude::*;
use log::debug;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::fs;
use tokio::sync::RwLock;
use tokio::time::{interval, timeout, Duration};
pub mod channel;
pub mod chanserv;
pub mod command;
pub mod config;
pub mod git;
pub type LockedState = Arc<RwLock<BotState>>;
use channel::ConfiguredChannel;
use config::TrustLevel;
#[derive(Default)]
pub struct BotState {
pub chanserv: Option<chanserv::Message>,
pub channels: HashMap<String, channel::ManagedChannel>,
pub botconfig: config::BotConfig,
}
impl BotState {
pub fn is_channel_done(&self, channel: &str) -> bool {
if let Some(managed_channel) = self.channels.get(channel) {
managed_channel.is_done()
} else {
false
}
}
pub fn is_flags_done(&self, channel: &str) -> bool {
if let Some(managed_channel) = self.channels.get(channel) {
managed_channel.flags_done
} else {
false
}
}
pub fn is_founder_on(&self, channel: &str, username: &str) -> bool {
if let Some(managed_channel) = self.channels.get(channel) {
if let Some(flags) = managed_channel.current.get(username) {
return flags.contains(&'F');
}
}
false
}
}
async fn wait_for_op(client: &Client, channel: &str) -> Result<()> {
let tmt =
timeout(Duration::from_secs(5), _wait_for_op(client, channel)).await;
if tmt.is_err() {
debug!("Timeout getting ops for {}", channel);
Err(anyhow!("Unable to get opped in {}", channel))
} else {
Ok(())
}
}
async fn _wait_for_op(client: &Client, channel: &str) {
if !is_opped_in(client, channel) {
debug!("Getting ops in {}", channel);
client
.send_privmsg("ChanServ", format!("op {}", channel))
.unwrap();
} else {
return;
}
let mut interval = interval(Duration::from_millis(200));
loop {
if is_opped_in(client, channel) {
break;
}
debug!("Not opped in {} yet.", channel);
interval.tick().await;
}
}
async fn read_channel_config(
dir: &str,
channel: &str,
) -> Result<ConfiguredChannel> {
Ok(toml::from_str(
&fs::read_to_string(format!(
"{}/channels/{}.toml",
dir,
channel.trim_start_matches('#')
))
.await?,
)?)
}
fn is_opped_in(client: &Client, channel: &str) -> bool {
if let Some(users) = client.list_users(channel) {
for user in users {
if user.get_nickname() == client.current_nickname() {
return user.access_levels().contains(&AccessLevel::Oper);
}
}
}
false
}
pub fn extract_account(message: &Message) -> Option<String> {
if let Some(tags) = &message.tags {
for tag in tags {
if tag.0 == "account" {
if let Some(name) = &tag.1 {
return Some(name.to_string());
}
}
}
}
None
}
pub async fn is_trusted(
state: &LockedState,
message: &Message,
level: TrustLevel,
) -> bool {
if let Some(account) = extract_account(message) {
let list = match level {
TrustLevel::Owner => state.read().await.botconfig.owners.clone(),
TrustLevel::Trusted => state.read().await.botconfig.trusted.clone(),
};
list.contains(&account)
} else {
false
}
}