Skip to main content

legion_protocol/
replies.rs

1//! IRC numeric replies and error codes
2//!
3//! This module contains the standard IRC numeric reply codes and error messages
4//! as defined in RFC 1459, RFC 2812, and various IRCv3 specifications.
5
6use crate::message::IrcMessage;
7
8/// IRC numeric replies and error codes
9#[derive(Debug, Clone)]
10pub enum Reply {
11    // Welcome sequence (001-005)
12    /// 001 RPL_WELCOME
13    Welcome { nick: String, network: String },
14    /// 002 RPL_YOURHOST
15    YourHost { nick: String, servername: String, version: String },
16    /// 003 RPL_CREATED
17    Created { nick: String, date: String },
18    /// 004 RPL_MYINFO
19    MyInfo { nick: String, servername: String, version: String, usermodes: String, chanmodes: String },
20    /// 005 RPL_ISUPPORT
21    ISupport { nick: String, tokens: Vec<String> },
22    
23    // Channel operations (331-366)
24    /// 331 RPL_NOTOPIC
25    NoTopic { nick: String, channel: String },
26    /// 332 RPL_TOPIC
27    Topic { nick: String, channel: String, topic: String },
28    /// 353 RPL_NAMREPLY
29    NamReply { nick: String, symbol: char, channel: String, names: Vec<String> },
30    /// 366 RPL_ENDOFNAMES
31    EndOfNames { nick: String, channel: String },
32    
33    // MOTD (372-376, 422)
34    /// 375 RPL_MOTDSTART
35    MotdStart { nick: String, server: String },
36    /// 372 RPL_MOTD
37    Motd { nick: String, line: String },
38    /// 376 RPL_ENDOFMOTD
39    EndOfMotd { nick: String },
40    /// 422 ERR_NOMOTD
41    NoMotd { nick: String },
42    
43    // Error replies (400-599)
44    /// 401 ERR_NOSUCHNICK
45    NoSuchNick { nick: String, target: String },
46    /// 403 ERR_NOSUCHCHANNEL
47    NoSuchChannel { nick: String, channel: String },
48    /// 404 ERR_CANNOTSENDTOCHAN
49    CannotSendToChan { nick: String, channel: String },
50    /// 442 ERR_NOTONCHANNEL
51    NotOnChannel { nick: String, channel: String },
52    /// 433 ERR_NICKNAMEINUSE
53    NicknameInUse { nick: String, attempted: String },
54    /// 461 ERR_NEEDMOREPARAMS
55    NeedMoreParams { nick: String, command: String },
56    /// 462 ERR_ALREADYREGISTERED
57    AlreadyRegistered { nick: String },
58    /// 421 ERR_UNKNOWNCOMMAND
59    UnknownCommand { nick: String, command: String },
60    /// 464 ERR_PASSWDMISMATCH
61    PasswdMismatch { nick: String },
62    /// 451 ERR_NOTREGISTERED
63    NotRegistered { nick: String },
64    
65    // Additional replies for compatibility
66    /// 432 ERR_ERRONEUSNICKNAME
67    ErroneousNickname { nick: String, attempted: String },
68    /// 475 ERR_BADCHANNELKEY
69    BadChannelKey { nick: String, channel: String },
70    /// 471 ERR_CHANNELISFULL
71    ChannelIsFull { nick: String, channel: String },
72    /// 482 ERR_CHANOPRIVSNEEDED
73    ChanOpPrivsNeeded { nick: String, channel: String },
74    /// 441 ERR_USERNOTINCHANNEL
75    UserNotInChannel { nick: String, target: String, channel: String },
76    /// 324 RPL_CHANNELMODEIS
77    ChannelModeIs { nick: String, channel: String, modes: String, params: Vec<String> },
78    /// 322 RPL_LIST
79    List { nick: String, channel: String, visible: usize, topic: String },
80    /// 315 RPL_ENDOFWHO
81    EndOfWho { nick: String, target: String },
82    /// 311 RPL_WHOISUSER
83    WhoisUser { nick: String, target: String, username: String, host: String, realname: String },
84    /// 312 RPL_WHOISSERVER
85    WhoisServer { nick: String, target: String, server: String, info: String },
86    /// 318 RPL_ENDOFWHOIS
87    EndOfWhois { nick: String, target: String },
88    /// 321 RPL_LISTSTART
89    ListStart { nick: String },
90    /// 323 RPL_LISTEND
91    ListEnd { nick: String },
92}
93
94impl Reply {
95    /// Convert reply to IRC message
96    pub fn to_message(&self, server_name: &str) -> IrcMessage {
97        match self {
98            Reply::Welcome { nick, network } => {
99                IrcMessage::new("001")
100                    .with_prefix(server_name)
101                    .with_params(vec![
102                        nick.clone(),
103                        format!("Welcome to the {} IRC Network, {}", network, nick),
104                    ])
105            }
106            Reply::YourHost { nick, servername, version } => {
107                IrcMessage::new("002")
108                    .with_prefix(server_name)
109                    .with_params(vec![
110                        nick.clone(),
111                        format!("Your host is {}, running version {}", servername, version),
112                    ])
113            }
114            Reply::Created { nick, date } => {
115                IrcMessage::new("003")
116                    .with_prefix(server_name)
117                    .with_params(vec![
118                        nick.clone(),
119                        format!("This server was created {}", date),
120                    ])
121            }
122            Reply::MyInfo { nick, servername, version, usermodes, chanmodes } => {
123                IrcMessage::new("004")
124                    .with_prefix(server_name)
125                    .with_params(vec![
126                        nick.clone(),
127                        servername.clone(),
128                        version.clone(),
129                        usermodes.clone(),
130                        chanmodes.clone(),
131                    ])
132            }
133            Reply::ISupport { nick, tokens } => {
134                let mut params = vec![nick.clone()];
135                params.extend(tokens.clone());
136                params.push("are supported by this server".to_string());
137                IrcMessage::new("005")
138                    .with_prefix(server_name)
139                    .with_params(params)
140            }
141            Reply::NoTopic { nick, channel } => {
142                IrcMessage::new("331")
143                    .with_prefix(server_name)
144                    .with_params(vec![
145                        nick.clone(),
146                        channel.clone(),
147                        "No topic is set".to_string(),
148                    ])
149            }
150            Reply::Topic { nick, channel, topic } => {
151                IrcMessage::new("332")
152                    .with_prefix(server_name)
153                    .with_params(vec![
154                        nick.clone(),
155                        channel.clone(),
156                        topic.clone(),
157                    ])
158            }
159            Reply::NamReply { nick, symbol, channel, names } => {
160                let mut params = vec![
161                    nick.clone(),
162                    symbol.to_string(),
163                    channel.clone(),
164                ];
165                let names_str = names.join(" ");
166                params.push(names_str);
167                IrcMessage::new("353")
168                    .with_prefix(server_name)
169                    .with_params(params)
170            }
171            Reply::EndOfNames { nick, channel } => {
172                IrcMessage::new("366")
173                    .with_prefix(server_name)
174                    .with_params(vec![
175                        nick.clone(),
176                        channel.clone(),
177                        "End of /NAMES list".to_string(),
178                    ])
179            }
180            Reply::MotdStart { nick, server } => {
181                IrcMessage::new("375")
182                    .with_prefix(server_name)
183                    .with_params(vec![
184                        nick.clone(),
185                        format!("- {} Message of the day -", server),
186                    ])
187            }
188            Reply::Motd { nick, line } => {
189                IrcMessage::new("372")
190                    .with_prefix(server_name)
191                    .with_params(vec![
192                        nick.clone(),
193                        format!("- {}", line),
194                    ])
195            }
196            Reply::EndOfMotd { nick } => {
197                IrcMessage::new("376")
198                    .with_prefix(server_name)
199                    .with_params(vec![
200                        nick.clone(),
201                        "End of /MOTD command".to_string(),
202                    ])
203            }
204            Reply::NoMotd { nick } => {
205                IrcMessage::new("422")
206                    .with_prefix(server_name)
207                    .with_params(vec![
208                        nick.clone(),
209                        "MOTD File is missing".to_string(),
210                    ])
211            }
212            Reply::NoSuchNick { nick, target } => {
213                IrcMessage::new("401")
214                    .with_prefix(server_name)
215                    .with_params(vec![
216                        nick.clone(),
217                        target.clone(),
218                        "No such nick/channel".to_string(),
219                    ])
220            }
221            Reply::NoSuchChannel { nick, channel } => {
222                IrcMessage::new("403")
223                    .with_prefix(server_name)
224                    .with_params(vec![
225                        nick.clone(),
226                        channel.clone(),
227                        "No such channel".to_string(),
228                    ])
229            }
230            Reply::CannotSendToChan { nick, channel } => {
231                IrcMessage::new("404")
232                    .with_prefix(server_name)
233                    .with_params(vec![
234                        nick.clone(),
235                        channel.clone(),
236                        "Cannot send to channel".to_string(),
237                    ])
238            }
239            Reply::NotOnChannel { nick, channel } => {
240                IrcMessage::new("442")
241                    .with_prefix(server_name)
242                    .with_params(vec![
243                        nick.clone(),
244                        channel.clone(),
245                        "You're not on that channel".to_string(),
246                    ])
247            }
248            Reply::NicknameInUse { nick, attempted } => {
249                IrcMessage::new("433")
250                    .with_prefix(server_name)
251                    .with_params(vec![
252                        nick.clone(),
253                        attempted.clone(),
254                        "Nickname is already in use".to_string(),
255                    ])
256            }
257            Reply::NeedMoreParams { nick, command } => {
258                IrcMessage::new("461")
259                    .with_prefix(server_name)
260                    .with_params(vec![
261                        nick.clone(),
262                        command.clone(),
263                        "Not enough parameters".to_string(),
264                    ])
265            }
266            Reply::AlreadyRegistered { nick } => {
267                IrcMessage::new("462")
268                    .with_prefix(server_name)
269                    .with_params(vec![
270                        nick.clone(),
271                        "You may not reregister".to_string(),
272                    ])
273            }
274            Reply::UnknownCommand { nick, command } => {
275                IrcMessage::new("421")
276                    .with_prefix(server_name)
277                    .with_params(vec![
278                        nick.clone(),
279                        command.clone(),
280                        "Unknown command".to_string(),
281                    ])
282            }
283            Reply::PasswdMismatch { nick } => {
284                IrcMessage::new("464")
285                    .with_prefix(server_name)
286                    .with_params(vec![
287                        nick.clone(),
288                        "Password incorrect".to_string(),
289                    ])
290            }
291            Reply::NotRegistered { nick } => {
292                IrcMessage::new("451")
293                    .with_prefix(server_name)
294                    .with_params(vec![
295                        nick.clone(),
296                        "You have not registered".to_string(),
297                    ])
298            }
299            Reply::ErroneousNickname { nick, attempted } => {
300                IrcMessage::new("432")
301                    .with_prefix(server_name)
302                    .with_params(vec![
303                        nick.clone(),
304                        attempted.clone(),
305                        "Erroneous nickname".to_string(),
306                    ])
307            }
308            Reply::BadChannelKey { nick, channel } => {
309                IrcMessage::new("475")
310                    .with_prefix(server_name)
311                    .with_params(vec![
312                        nick.clone(),
313                        channel.clone(),
314                        "Cannot join channel (+k)".to_string(),
315                    ])
316            }
317            Reply::ChannelIsFull { nick, channel } => {
318                IrcMessage::new("471")
319                    .with_prefix(server_name)
320                    .with_params(vec![
321                        nick.clone(),
322                        channel.clone(),
323                        "Cannot join channel (+l)".to_string(),
324                    ])
325            }
326            Reply::ChanOpPrivsNeeded { nick, channel } => {
327                IrcMessage::new("482")
328                    .with_prefix(server_name)
329                    .with_params(vec![
330                        nick.clone(),
331                        channel.clone(),
332                        "You're not channel operator".to_string(),
333                    ])
334            }
335            Reply::UserNotInChannel { nick, target, channel } => {
336                IrcMessage::new("441")
337                    .with_prefix(server_name)
338                    .with_params(vec![
339                        nick.clone(),
340                        target.clone(),
341                        channel.clone(),
342                        "They aren't on that channel".to_string(),
343                    ])
344            }
345            Reply::ChannelModeIs { nick, channel, modes, params } => {
346                let mut msg_params = vec![nick.clone(), channel.clone(), modes.clone()];
347                msg_params.extend(params.clone());
348                IrcMessage::new("324")
349                    .with_prefix(server_name)
350                    .with_params(msg_params)
351            }
352            Reply::List { nick, channel, visible, topic } => {
353                IrcMessage::new("322")
354                    .with_prefix(server_name)
355                    .with_params(vec![
356                        nick.clone(),
357                        channel.clone(),
358                        visible.to_string(),
359                        topic.clone(),
360                    ])
361            }
362            Reply::EndOfWho { nick, target } => {
363                IrcMessage::new("315")
364                    .with_prefix(server_name)
365                    .with_params(vec![
366                        nick.clone(),
367                        target.clone(),
368                        "End of /WHO list".to_string(),
369                    ])
370            }
371            Reply::WhoisUser { nick, target, username, host, realname } => {
372                IrcMessage::new("311")
373                    .with_prefix(server_name)
374                    .with_params(vec![
375                        nick.clone(),
376                        target.clone(),
377                        username.clone(),
378                        host.clone(),
379                        "*".to_string(),
380                        realname.clone(),
381                    ])
382            }
383            Reply::WhoisServer { nick, target, server, info } => {
384                IrcMessage::new("312")
385                    .with_prefix(server_name)
386                    .with_params(vec![
387                        nick.clone(),
388                        target.clone(),
389                        server.clone(),
390                        info.clone(),
391                    ])
392            }
393            Reply::EndOfWhois { nick, target } => {
394                IrcMessage::new("318")
395                    .with_prefix(server_name)
396                    .with_params(vec![
397                        nick.clone(),
398                        target.clone(),
399                        "End of /WHOIS list".to_string(),
400                    ])
401            }
402            Reply::ListStart { nick } => {
403                IrcMessage::new("321")
404                    .with_prefix(server_name)
405                    .with_params(vec![
406                        nick.clone(),
407                        "Channel".to_string(),
408                        "Users  Name".to_string(),
409                    ])
410            }
411            Reply::ListEnd { nick } => {
412                IrcMessage::new("323")
413                    .with_prefix(server_name)
414                    .with_params(vec![
415                        nick.clone(),
416                        "End of /LIST".to_string(),
417                    ])
418            }
419        }
420    }
421}
422
423impl From<Reply> for crate::IrcMessage {
424    fn from(reply: Reply) -> Self {
425        reply.to_message("ironchatd.local")
426    }
427}