rustacean_roulette/
lib.rs1mod commands;
2mod defaults;
3
4pub use commands::Commands;
5use frankenstein::{client_reqwest::Bot, methods::{SetMyCommandsParams, SetMyDefaultAdministratorRightsParams}, types::ChatAdministratorRights, AsyncTelegramApi, Error};
6use rand::{Rng, seq::index::sample};
7use serde::Deserialize;
8use std::time::{SystemTime, UNIX_EPOCH};
9
10#[derive(Deserialize)]
12pub struct Config {
13 pub token: String,
15 #[serde(default)]
17 pub game: Roulette,
18 #[serde(default)]
20 pub groups: Vec<GroupConfig>,
21}
22
23#[derive(Clone, Debug, Deserialize)]
25pub struct Roulette {
26 #[serde(default = "defaults::chambers")]
28 chambers: usize,
29 #[serde(default = "defaults::bullets")]
31 bullets: usize,
32 #[serde(default = "defaults::min_mute_time")]
34 min_mute_time: u32,
35 #[serde(default = "defaults::max_mute_time")]
37 max_mute_time: u32,
38 #[serde(skip)]
40 contents: Vec<bool>,
41 #[serde(skip)]
43 position: usize,
44}
45
46impl Roulette {
47 pub fn restart(&mut self) {
49 assert!(
51 self.chambers > 0,
52 "Number of chambers must be greater than 0"
53 );
54 assert!(self.bullets > 0, "Number of bullets must be greater than 0");
55 assert!(
56 self.bullets <= self.chambers,
57 "Number of bullets must be less than or equal to number of chambers"
58 );
59 assert!(
60 self.min_mute_time >= 30,
61 "Minimum mute time must be greater than or equal to 30 seconds"
62 );
63 assert!(
64 self.max_mute_time <= 3600,
65 "Maximum mute time must be less than or equal to 3600 seconds"
66 ); assert!(
68 self.min_mute_time <= self.max_mute_time,
69 "Minimum mute time must be less than or equal to maximum mute time"
70 );
71
72 self.position = 0;
73 self.contents.resize(self.chambers, false);
74
75 let mut rng = rand::rng();
77 let selected = sample(&mut rng, self.chambers, self.bullets);
78 for i in selected {
79 self.contents[i] = true;
80 }
81 }
82
83 pub fn info(&self) -> (usize, usize) {
85 (self.bullets, self.chambers)
86 }
87
88 pub fn random_mute_until(&self) -> (u64, u64) {
90 let mut rng = rand::rng();
92 let duration: u64 = rng
93 .random_range(self.min_mute_time..=self.max_mute_time)
94 .into();
95 let now = SystemTime::now()
97 .duration_since(UNIX_EPOCH)
98 .expect("Time went backwards")
99 .as_secs();
100 (duration, now + duration)
101 }
102
103 pub fn fire(&mut self) -> Option<bool> {
109 if self.peek().0 == 0 {
110 return None;
112 }
113
114 let result = self.contents[self.position];
115 self.position += 1;
116
117 Some(result)
118 }
119
120 pub fn peek(&self) -> (usize, usize) {
122 let filled = self
123 .contents
124 .iter()
125 .skip(self.position)
126 .filter(|&&x| x)
127 .count();
128 let left = self.contents.len() - self.position;
129 (filled, left)
130 }
131}
132
133impl Default for Roulette {
134 fn default() -> Self {
135 Roulette {
136 chambers: defaults::chambers(),
137 bullets: defaults::bullets(),
138 min_mute_time: defaults::min_mute_time(),
139 max_mute_time: defaults::max_mute_time(),
140 contents: vec![],
141 position: 0,
142 }
143 }
144}
145
146#[derive(Debug, Deserialize)]
148pub struct GroupConfig {
149 pub id: i64,
151 chambers: Option<usize>,
153 bullets: Option<usize>,
155 min_mute_time: Option<u32>,
157 max_mute_time: Option<u32>,
159}
160
161impl GroupConfig {
162 pub fn resolve(&self, default: &Roulette) -> Roulette {
164 let Self {
165 chambers,
166 bullets,
167 min_mute_time,
168 max_mute_time,
169 ..
170 } = self;
171 let (chambers, bullets, min_mute_time, max_mute_time) = (
172 chambers.unwrap_or(default.chambers),
173 bullets.unwrap_or(default.bullets),
174 min_mute_time.unwrap_or(default.min_mute_time),
175 max_mute_time.unwrap_or(default.max_mute_time),
176 );
177 Roulette {
178 chambers,
179 bullets,
180 min_mute_time,
181 max_mute_time,
182 ..Default::default()
183 }
184 }
185}
186
187pub async fn init_commands_and_rights(bot: &Bot) -> Result<(), Error> {
189 let commands_param = SetMyCommandsParams::builder()
190 .commands(Commands::list())
191 .build();
192 bot.set_my_commands(&commands_param).await?;
193
194 let rights = ChatAdministratorRights::builder()
195 .is_anonymous(false)
196 .can_manage_chat(false)
197 .can_delete_messages(false)
198 .can_manage_video_chats(false)
199 .can_restrict_members(true) .can_promote_members(false)
201 .can_change_info(false)
202 .can_invite_users(false)
203 .build();
204 let rights_param = SetMyDefaultAdministratorRightsParams::builder()
205 .rights(rights)
206 .build();
207 bot.set_my_default_administrator_rights(&rights_param).await?;
208
209 Ok(())
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_fire() {
218 let mut roulette = Roulette {
219 contents: vec![false, true, false],
220 position: 0,
221 chambers: 3,
222 bullets: 1,
223 ..Default::default()
224 };
225
226 assert_eq!(roulette.fire(), Some(false));
227 assert_eq!(roulette.fire(), Some(true));
228 assert_eq!(roulette.fire(), None);
230 assert_eq!(roulette.fire(), None);
231 }
232
233 #[test]
234 fn test_restart() {
235 let mut roulette = Roulette::default();
236
237 roulette.restart();
238 assert_eq!(roulette.contents.len(), 6);
239 assert_eq!(roulette.contents.iter().filter(|&&x| x).count(), 2);
240 assert_eq!(roulette.position, 0);
241 }
242}