mirror of https://github.com/PiyushXCoder/lupt.git
Basics
This commit is contained in:
commit
d4ef5636ef
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "lupt"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Chat app for lupt(लुप्त) users!"
|
||||||
|
authors = ["Piyush Raj <piyush.raj.kit@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix = "0.10"
|
||||||
|
actix-web = "3"
|
||||||
|
actix-web-actors = "3"
|
||||||
|
actix-broker = "0.3.1"
|
||||||
|
actix-files = "0.5.0"
|
||||||
|
|
||||||
|
clap = "2.33.3"
|
||||||
|
serde = "1.0.123"
|
||||||
|
serde_json = "1.0.62"
|
||||||
|
rand = "0.8.3"
|
||||||
|
futures = "0.3.12"
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
//! Chat Pinnd(पिण्ड) is Actor to manage Websocket Chat related action
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use actix::{Actor, Addr, Context, Handler, Message};
|
||||||
|
|
||||||
|
use crate::ws_sansad;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct ChatPinnd {
|
||||||
|
grih: HashMap<i32, Grih>,
|
||||||
|
vyaktigat_waitlist: Vec<Addr<ws_sansad::WsSansad>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Grih {
|
||||||
|
name: Option<String>,
|
||||||
|
length: Option<usize>,
|
||||||
|
clients: Vec<Addr<ws_sansad::WsSansad>>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler Messages
|
||||||
|
pub struct Join {
|
||||||
|
pub grih: JoinType,
|
||||||
|
pub length: Option<usize>,
|
||||||
|
pub addr: Addr<ws_sansad::WsSansad>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum JoinType {
|
||||||
|
Name(String),
|
||||||
|
Kunjika(i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Text {
|
||||||
|
pub grih_kunjika: i32,
|
||||||
|
pub sender_name: String,
|
||||||
|
pub text: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Delete {
|
||||||
|
pub grih_kunjika: i32,
|
||||||
|
pub addr: Addr<ws_sansad::WsSansad>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for ChatPinnd {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Join> for ChatPinnd {
|
||||||
|
type Result = Option<i32>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Join, _: &mut Self::Context) -> Self::Result {
|
||||||
|
println!("Came to join");
|
||||||
|
match msg.grih {
|
||||||
|
JoinType::Name(name) => {
|
||||||
|
let mat = Some(name.clone());
|
||||||
|
if let Some((kunjika, grih)) =
|
||||||
|
self.grih.iter_mut().find(|(_, g)| g.name == mat) {
|
||||||
|
if let Some(val) = grih.length {
|
||||||
|
if grih.clients.len()+1 == val {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grih.clients.push(msg.addr.clone());
|
||||||
|
return Some(*kunjika);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut kunjika: i32 = rand::random::<i32>();
|
||||||
|
while self.grih.contains_key(&kunjika) {
|
||||||
|
kunjika = rand::random::<i32>();
|
||||||
|
}
|
||||||
|
println!("Creating {}", name);
|
||||||
|
self.grih.insert(kunjika, Grih {
|
||||||
|
name: Some(name),
|
||||||
|
length: msg.length,
|
||||||
|
clients: vec![msg.addr]
|
||||||
|
});
|
||||||
|
|
||||||
|
return Some(kunjika);
|
||||||
|
}, JoinType::Kunjika(kunjika) => {
|
||||||
|
match self.grih.get_mut(&kunjika) {
|
||||||
|
Some(grih) => {
|
||||||
|
if let Some(val) = grih.length {
|
||||||
|
if grih.clients.len()+1 == val {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grih.clients.push(msg.addr.clone());
|
||||||
|
}, None => {
|
||||||
|
self.grih.insert(kunjika, Grih {
|
||||||
|
name: None,
|
||||||
|
length: msg.length,
|
||||||
|
clients: vec![msg.addr]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(kunjika);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Text> for ChatPinnd {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Text, _: &mut Self::Context) -> Self::Result {
|
||||||
|
println!("Here to text");
|
||||||
|
if let Some(grih) = self.grih.get(&msg.grih_kunjika) {
|
||||||
|
for client in grih.clients.iter() {
|
||||||
|
client.do_send(ws_sansad::Text {
|
||||||
|
sender: msg.sender_name.clone(),
|
||||||
|
text: msg.text.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Delete> for ChatPinnd {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Delete, _: &mut Self::Context) -> Self::Result {
|
||||||
|
if let Some(grih) = self.grih.get_mut(&msg.grih_kunjika) {
|
||||||
|
if let Some(i) = grih.clients.iter().position(|x| x == &msg.addr) {
|
||||||
|
grih.clients.remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if grih.clients.len() == 0 {
|
||||||
|
self.grih.remove(&msg.grih_kunjika);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatPinnd {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ChatPinnd {
|
||||||
|
grih: HashMap::new(),
|
||||||
|
vyaktigat_waitlist: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start() -> Addr<ChatPinnd> {
|
||||||
|
ChatPinnd::new().start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for Join { type Result = Option<i32>; }
|
||||||
|
impl Message for Text { type Result = (); }
|
||||||
|
impl Message for Delete { type Result = (); }
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub static_path: String,
|
||||||
|
pub bind_address: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let matches = App::new("Lupt (लुप्त)")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.author(env!("CARGO_PKG_AUTHORS"))
|
||||||
|
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||||
|
.arg(Arg::with_name("static_path")
|
||||||
|
.short("b")
|
||||||
|
.long("static_path")
|
||||||
|
.value_name("DIR")
|
||||||
|
.help("Path of directory with index.html")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true))
|
||||||
|
.arg(Arg::with_name("bind_address")
|
||||||
|
.short("a")
|
||||||
|
.long("bind_address")
|
||||||
|
.value_name("ADDRESS")
|
||||||
|
.help("Address to bind for server")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true))
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
Config {
|
||||||
|
static_path: matches.value_of("static_path").unwrap().to_owned(),
|
||||||
|
bind_address: matches.value_of("bind_address").unwrap().to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
use actix::Addr;
|
||||||
|
use actix_web::{App, Error, HttpRequest, HttpResponse, HttpServer, web};
|
||||||
|
use actix_files as fs;
|
||||||
|
use actix_web_actors::ws;
|
||||||
|
use chat_pinnd::ChatPinnd;
|
||||||
|
use ws_sansad::WsSansad;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod ws_sansad;
|
||||||
|
mod chat_pinnd;
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
let config = config::Config::new();
|
||||||
|
let addr = web::Data::new(ChatPinnd::start());
|
||||||
|
let static_path = config.static_path;
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.app_data(addr.clone())
|
||||||
|
.service(web::resource("/ws/").route(web::get().to(ws_index)))
|
||||||
|
.service(fs::Files::new("/", &static_path).index_file("index.html"))
|
||||||
|
})
|
||||||
|
.bind(config.bind_address)?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ws_index(req: HttpRequest, stream: web::Payload, pinnd: web::Data<Addr<ChatPinnd>>) -> Result<HttpResponse, Error> {
|
||||||
|
let (addr, resp) = ws::start_with_addr(WsSansad::new(pinnd), &req, stream)?;
|
||||||
|
addr.do_send(ws_sansad::SelfAddr(addr.clone()));
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
//! Ws Sansad manage websocket of each client
|
||||||
|
use actix::{Actor, Addr, Handler, Message, Running, StreamHandler};
|
||||||
|
use actix_web::web;
|
||||||
|
use actix_web_actors::ws;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
use crate::chat_pinnd::ChatPinnd;
|
||||||
|
use crate::chat_pinnd as pd;
|
||||||
|
|
||||||
|
pub struct WsSansad {
|
||||||
|
name: String,
|
||||||
|
isthiti: Isthiti,
|
||||||
|
pinnd: web::Data<Addr<ChatPinnd>>,
|
||||||
|
addr: Option<Addr<Self>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum Isthiti {
|
||||||
|
None,
|
||||||
|
Grih(Grih),
|
||||||
|
// VraktigatWaitlist
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Grih {
|
||||||
|
kunjika: i32,
|
||||||
|
// name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler Messages
|
||||||
|
pub struct Text {
|
||||||
|
pub text: String,
|
||||||
|
pub sender: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SelfAddr(pub Addr<WsSansad>);
|
||||||
|
|
||||||
|
impl Actor for WsSansad {
|
||||||
|
type Context = ws::WebsocketContext<Self>;
|
||||||
|
|
||||||
|
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||||
|
futures::executor::block_on(self.end());
|
||||||
|
Running::Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSansad {
|
||||||
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
|
match msg {
|
||||||
|
Ok(ws::Message::Ping(msg)) => ctx.ping(&msg),
|
||||||
|
Ok(ws::Message::Text(msg)) => {
|
||||||
|
futures::executor::block_on(self.parse_text_handle(msg));
|
||||||
|
},
|
||||||
|
Ok(ws::Message::Close(msg)) => {
|
||||||
|
ctx.close(msg);
|
||||||
|
}
|
||||||
|
_ => ctx.close(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<Text> for WsSansad {
|
||||||
|
type Result = ();
|
||||||
|
fn handle(&mut self, msg: Text, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let json = json!({
|
||||||
|
"cmd": "text",
|
||||||
|
"text": msg.text,
|
||||||
|
"sender": msg.sender
|
||||||
|
});
|
||||||
|
ctx.text(json.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<SelfAddr> for WsSansad {
|
||||||
|
type Result = ();
|
||||||
|
fn handle(&mut self, msg: SelfAddr, _: &mut Self::Context) -> Self::Result {
|
||||||
|
self.addr = Some(msg.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsSansad {
|
||||||
|
pub fn new(pinnd: web::Data<Addr<ChatPinnd>>) -> Self {
|
||||||
|
WsSansad {
|
||||||
|
name: "()".to_owned(),
|
||||||
|
isthiti: Isthiti::None,
|
||||||
|
pinnd,
|
||||||
|
addr: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_text_handle(&mut self, msg: String) {
|
||||||
|
if let Ok(val) = serde_json::from_str::<Value>(&msg) {
|
||||||
|
match val.get("cmd").unwrap().as_str().unwrap() {
|
||||||
|
"name" => { self.name(val).await },
|
||||||
|
"join" => { self.join(val).await },
|
||||||
|
"text" => { self.text(val).await },
|
||||||
|
"end" => { self.end().await },
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn name(&mut self, val: Value) {
|
||||||
|
self.name = val.get("name").unwrap().as_str().unwrap().to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn text(&mut self, val: Value) {
|
||||||
|
let text = val.get("text").unwrap().as_str().unwrap().to_owned();
|
||||||
|
let grih_kunjika = match &self.isthiti {
|
||||||
|
Isthiti::Grih(g) => {
|
||||||
|
g.kunjika
|
||||||
|
}, Isthiti::None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pinnd.do_send(pd::Text {
|
||||||
|
grih_kunjika,
|
||||||
|
sender_name: self.name.clone(),
|
||||||
|
text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn join(&mut self, val: Value) {
|
||||||
|
let name = val.get("name").unwrap().as_str().unwrap().to_owned();
|
||||||
|
let length: Option<usize> = match val.get("length") {
|
||||||
|
Some(val) => Some(val.as_i64().unwrap() as usize),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let kunjika = self.pinnd.send(pd::Join{
|
||||||
|
grih: pd::JoinType::Name(name.clone()),
|
||||||
|
length,
|
||||||
|
addr: self.addr.clone().unwrap()
|
||||||
|
}).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
self.isthiti = Isthiti::Grih(Grih {
|
||||||
|
kunjika,
|
||||||
|
// name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn end(&mut self) {
|
||||||
|
if let Isthiti::Grih(val) = &mut self.isthiti {
|
||||||
|
self.pinnd.do_send(pd::Delete {
|
||||||
|
grih_kunjika: val.kunjika,
|
||||||
|
addr: self.addr.clone().unwrap()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for Text { type Result = (); }
|
||||||
|
impl Message for SelfAddr { type Result = (); }
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="shortcut icon" href="#">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// Create WebSocket connection.
|
||||||
|
const socket = new WebSocket('ws://localhost:8080/ws/');
|
||||||
|
|
||||||
|
// Connection opened
|
||||||
|
socket.addEventListener('open', function (event) {
|
||||||
|
socket.send('Hello Server!');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for messages
|
||||||
|
socket.addEventListener('message', function (event) {
|
||||||
|
console.log('Message from server ', event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
function wsend(p) {
|
||||||
|
socket.send(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function join(r) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
cmd: "join",
|
||||||
|
name: r
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(t) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
cmd: "text",
|
||||||
|
text: t
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function name(t) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
cmd: "name",
|
||||||
|
name: t
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue