commit so i can work elsewhere

This commit is contained in:
Arthur Beck 2025-03-04 06:56:22 -06:00
parent 58fa9ee185
commit 686d1d74fe
Signed by: ArthurB
GPG key ID: ACE3D14F5CEF14BF
2 changed files with 208 additions and 0 deletions

7
Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "rawnetchat"
version = "0.1.0"
edition = "2024"
[dependencies]
time = { version = "0.3.37", features = ["formatting", "local-offset"] }

201
src/main.rs Normal file
View file

@ -0,0 +1,201 @@
#![feature(random)]
#![feature(inline_const_pat)]
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::random;
use std::sync::{Arc, Mutex};
use std::thread::{self, sleep};
#[derive(Clone)]
enum UserStatus {
ONLINE,
DISCONNECTED,
IDLE,
CUSTOM(String),
}
impl UserStatus {
fn get_status_string(&self) -> String {
match self {
UserStatus::ONLINE => "\x1b[0m\x1b[32mOnline\x1b[0m".to_string(),
UserStatus::IDLE => "\x1b[0m\x1b[33mIdle\x1b[0m".to_string(),
UserStatus::DISCONNECTED => "\x1b[0m\x1b[31mDisconnected\x1b[0m".to_string(),
UserStatus::CUSTOM(val) => format!("\x1b[0m\x1b[34m{}\x1b[0m", val)
}
}
}
#[derive(Clone)]
struct User {
name: String,
status: UserStatus,
}
#[derive(Clone)]
struct Message {
sender: User,
contents: String,
timestamp: time::OffsetDateTime,
}
type Channel = Arc<Mutex<UnwrappedChannel>>;
#[derive(Clone)]
struct UnwrappedChannel {
name: String,
messages: Vec<Message>,
online_users: Vec<User>,
}
fn handle_client(mut stream: TcpStream, channel: Channel) {
stream
.write_all(b"\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n")
.unwrap();
let mut buf = [0u8; 30];
let mut i = 0usize;
let mut buf1 = [0u8];
while buf1[0] != b'\n' || i == 0 {
stream.read_exact(&mut buf1).unwrap();
if buf1[0] == b'\n' && i == 0 {
stream.write_all(
b"\x1b[0m\x1b[31;1mEnter at least one character.\n\x1b[0m\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n"
).unwrap();
buf = [0u8; 30];
buf1[0] = b'\0';
continue;
} else if buf1[0] == b'\n' && i >= 30 {
stream.write_all(
b"\x1b[0m\x1b[31;1mEnter less than 30 characters.\n\x1b[0m\x1b[33mEnter your chosen nickname(up to 30 characters):\x1b[0m\x1b[4m\n"
).unwrap();
buf = [0u8; 30];
buf1[0] = b'\0';
i = 0;
continue;
} else if buf1[0] == b'\n' {
break;
}
if i < 30 {
buf[i] = buf1[0];
i += 1;
}
}
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[32;1mWelcome, {}! Joining server \"{}\".\x1b[0m\n",
String::from_utf8_lossy(&(buf[..i])),
channel.lock().unwrap().name
))
.unwrap();
let user = User {
name: String::from_utf8_lossy(&(buf[..i])).to_string(),
status: UserStatus::ONLINE,
};
channel.lock().unwrap().online_users.push(user.clone());
{
let channel = channel.clone();
let mut stream = stream.try_clone().unwrap();
let time_format = time::format_description::well_known::Rfc2822;
thread::spawn(move || {
let mut current_message_count = channel.lock().unwrap().messages.len();
loop {
let new_message_count = channel.lock().unwrap().messages.len();
if current_message_count != new_message_count {
let new_messages = &channel.lock().unwrap().messages[current_message_count..];
for msg in new_messages {
stream
.write_fmt(format_args!(
"\x1b[0m\x1b[34m[{}]\x1b[0m \x1b[1m{}\x1b[0m \x1b[31m⇛\x1b[0m \x1b[35m{}\x1b[0m\n",
msg.timestamp.format(&time_format).unwrap(),
msg.sender.name,
msg.contents
))
.unwrap();
}
current_message_count = new_message_count;
}
// so that not all the threads will sync at once
let randomval = random::random::<u64>() / (18446744073709551615u64 / 20);
sleep(std::time::Duration::from_millis(240 + randomval));
}
});
}
let mut buf = Vec::<u8>::new();
let mut buf1 = [0u8];
loop {
stream.read_exact(&mut buf1).unwrap();
if buf1[0] == b'\x08' {
buf.pop();
}
if buf1[0] == b'\n' && buf.len() > 0 {
let contents = String::from_utf8_lossy(&buf).to_string();
if !contents.starts_with("/") {
channel.lock().unwrap().messages.push(Message {
sender: user.clone(),
contents,
timestamp: time::OffsetDateTime::now_local().unwrap(),
});
} else {
if contents.starts_with("/help") || contents.starts_with("/?") {
stream
.write_all(
b"\x1b[0m\x1b[32mValid commands:\n \
/help, /?: Print this help.\n \
/status online|idle|disconnected|(any value): Set your status.\n \
/user nick: Get information about a user.\x1b[0m",
)
.unwrap();
} else if contents.starts_with("/status") {
let contents = contents.split(" ").collect::<Vec<&str>>();
if contents.len() == 1 {
stream
.write_fmt(
format_args!("\x1b[0m\x1b[32mYou are currently marked as {}\n\x1b[0m"),
)
.unwrap();
}
} else {
stream
.write_all(b"\x1b[0m\x1b[31;1mThat's an invalid command!\n\x1b[0m")
.unwrap();
}
}
buf.clear();
}
if buf1[0] == b'\n' {
continue;
}
buf.push(buf1[0]);
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("0.0.0.0:7867")?;
let channel: Channel = Arc::new(Mutex::new(UnwrappedChannel {
name: "Rawnetchat".to_string(),
messages: vec![],
online_users: vec![],
}));
for stream in listener.incoming() {
let channel: Channel = channel.clone();
thread::spawn(|| {
handle_client(stream.unwrap(), channel);
});
}
Ok(())
}