extern crate websocket;
#[macro_use] extern crate serde_json;
extern crate rustc_serialize;
use std::thread;
use rustc_serialize::json;
use websocket::{Client, Message, WebSocketStream};
use websocket::message::Type;
use websocket::client::request::Url;
use websocket::sender::Sender;
use websocket::receiver::Receiver;
use websocket::ws::sender::Sender as SenderTrait;
use websocket::ws::receiver::Receiver as ReceiverTrait;
use std::sync::Arc;
use std::sync::Mutex;
#[derive(Clone)]
pub struct ChatClient {
nick: String,
channel: String,
sender: Arc<Mutex<Sender<WebSocketStream>>>,
receiver: Arc<Mutex<Receiver<WebSocketStream>>>,
}
impl ChatClient {
pub fn new(nick: &str, channel: &str) -> ChatClient {
let url = Url::parse("wss://hack.chat/chat-ws").unwrap();
let request = Client::connect(url).unwrap();
let response = request.send().unwrap();
let client = response.begin();
let (mut sender, receiver) = client.split();
let join_packet = json!({
"cmd": "join",
"nick": nick,
"channel": channel
});
let message = Message::text(join_packet.to_string());
sender.send_message(&message).unwrap();
return ChatClient {
nick: nick.to_string(),
channel: channel.to_string(),
sender: Arc::new(Mutex::new(sender)),
receiver: Arc::new(Mutex::new(receiver))
};
}
pub fn send_message(&mut self, message: String) {
let chat_packet = json!({
"cmd": "chat",
"text": message
});
let message = Message::text(chat_packet.to_string());
self.sender.lock().unwrap().send_message(&message).unwrap();
}
fn send_ping(&mut self) {
let ping_packet = json!({
"cmd": "ping"
});
let message = Message::text(ping_packet.to_string());
self.sender.lock().unwrap().send_message(&message).unwrap();
}
pub fn send_stats_request(&mut self) {
let stats_packet = json!({
"cmd": "stats"
});
let message = Message::text(stats_packet.to_string());
self.sender.lock().unwrap().send_message(&message).unwrap();
}
pub fn start_ping_thread(&mut self) {
let mut chat_clone = self.clone();
thread::spawn(move|| {
loop {
thread::sleep_ms(60 * 1000);
chat_clone.send_ping();
}
});
}
pub fn iter(&mut self) -> ChatClient {
return self.clone();
}
}
impl Iterator for ChatClient {
type Item = ChatEvent;
fn next(&mut self) -> Option<ChatEvent> {
loop {
let message: Message = match self.receiver.lock().unwrap().recv_message() {
Ok(message) => message,
Err(e) => {
println!("{}", e);
continue;
}
};
match message.opcode {
Type::Text => {
let data = std::str::from_utf8(&*message.payload).unwrap();
let cmdpacket: serde_json::Value = match serde_json::from_slice(&*message.payload) {
Ok(packet) => packet,
Err(e) => {
println!("{}", e);
continue;
}
};
match cmdpacket.get("cmd").unwrap_or(&serde_json::Value::Null).as_str() {
Some("chat") => {
let decodedpacket: ChatPacket = json::decode(&data).unwrap();
if decodedpacket.nick != self.nick {
return Some(ChatEvent::Message (
decodedpacket.nick,
decodedpacket.text,
decodedpacket.trip.unwrap_or("".to_string())
));
}else {
continue;
}
},
Some("info") => {
let decodedpacket: InfoWarnPacket = json::decode(&data).unwrap();
return Some(ChatEvent::Info (
decodedpacket.text
));
},
Some("onlineAdd") => {
let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
return Some(ChatEvent::JoinRoom (
decodedpacket.nick
));
},
Some("onlineRemove") => {
let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
return Some(ChatEvent::LeaveRoom (
decodedpacket.nick
));
},
_ => {
println!("Unsupported message type");
continue;
}
}
},
Type::Ping => {
self.sender.lock().unwrap().send_message(&Message::pong(message.payload)).unwrap();
},
_ => {
return None;
}
};
return None;
}
}
}
pub enum ChatEvent {
Message (String, String, String),
JoinRoom (String),
LeaveRoom (String),
Info (String)
}
#[derive(RustcEncodable, RustcDecodable)]
struct GenericPacket {
cmd: String
}
#[derive(RustcDecodable)]
struct ChatPacket {
nick: String,
text: String,
trip: Option<String>
}
#[derive(RustcDecodable)]
struct OnlineChangePacket {
nick: String
}
#[derive(RustcDecodable)]
struct InfoWarnPacket {
text: String
}