Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add users support #6

Draft
wants to merge 27 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c8d0537
chore(deps): update to enoki/users todel
ooliver1 Jun 3, 2023
0c5fc7e
feat: add login
ooliver1 Jun 3, 2023
52fdc3b
feat: add signup
ooliver1 Jun 3, 2023
2dd2715
refactor: add header to message sending
ooliver1 Jun 3, 2023
3a98345
fix: just waiting 4000s, hold on
ooliver1 Jun 3, 2023
67e4b0b
feat: email verification
ooliver1 Jun 3, 2023
138135f
refactor: add better error handling to signup
ooliver1 Jun 4, 2023
07fa3ad
refactor: add better error handling to create_session
ooliver1 Jun 4, 2023
6ce8e2b
refactor: add better error handling to verification
ooliver1 Jun 4, 2023
6511748
fix: update todel
ooliver1 Jun 12, 2023
bd10ae6
feat: member list
ooliver1 Jun 16, 2023
22f1160
fix: properly hide offline users
ooliver1 Jun 16, 2023
592dc68
ok non-free os users
ooliver1 Jun 16, 2023
cce80c4
feat: make users list togglable
ooliver1 Jun 17, 2023
199729f
docs(readme): document keybinds
ooliver1 Jun 17, 2023
9d5957e
feat: add version check
ooliver1 Jun 17, 2023
a24195f
refactor: cleanup code
ooliver1 Jun 17, 2023
3a18c54
fix: mention pilfer --verify in response
ooliver1 Jun 17, 2023
c7bbe74
build(deps): im sure i removed re
ooliver1 Jun 17, 2023
c8ef931
refactor: convert appcontext to arc
ooliver1 Jun 17, 2023
454ee08
refactor: remove use of serde_json::json!
ooliver1 Jun 17, 2023
72e487e
refactor: compare versions with tuples
ooliver1 Jun 17, 2023
cd029f0
fix: only app in linux :)
ooliver1 Jun 17, 2023
edef88c
fixL what is a less than
ooliver1 Jun 19, 2023
96348e5
refactor: remove error version check
ooliver1 Aug 27, 2023
dfb875c
fix: use public todel repo
ooliver1 Oct 16, 2023
f858f2c
chore: appease Clippy
EnokiUN Jan 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 288 additions & 63 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pilfer"
version = "0.3.3"
version = "0.4.0-alpha1"
edition = "2021"
license = "MIT"
description = "A simple TUI frontend for Eludris"
Expand All @@ -19,15 +19,26 @@ keywords = ["eludris"]
[dependencies]
anyhow = "1.0.67"
crossterm = "0.25.0"
directories = "5.0.1"
discord-rich-presence = "0.2.3"
futures = "0.3.23"
lazy_static = "1.4.0"
log = "0.4.20"
notify-rust = "4.5.10"
rand = "0.8.5"
regex = "1.8.4"
reqwest = { version = "0.11.11", features = ["json"] }
rpassword = "7.2.0"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
todel = { git = "https://github.com/eludris/eludris", version = "0.3.3" }
tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread", "time"] }
todel = { git = "https://github.com/eludris/todel", version = "0.4.0-alpha1" }
tokio = { version = "1.20.1", features = [
"macros",
"rt-multi-thread",
"time",
"fs",
] }
tokio-tungstenite = { version = "0.17.2", features = ["native-tls"] }
toml = "0.7.4"
tui = "0.19.0"
unicode-width = "0.1.9"
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ of this repository.
Pilfer defaults to using @ooliver1's Eludris instance located at <https://eludris.tooty.xyz/>,
to change that overwrite the `INSTANCE_URL` environment variable.

You can also define a default name using the `PILFER_NAME` environment variable,
it has to be > 1 characters < 33 characters.
### Keybinds

| Keybind | Action |
| ---------------- | ------------------------ |
| Ctrl + C | Quit |
| Ctrl + L | Clear |
| Ctrl + \[space\] | Add new line |
| Ctrl + U | Toggle online users list |
182 changes: 135 additions & 47 deletions src/gateway.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
use std::{
sync::{atomic::AtomicBool, Arc, Mutex},
time::Duration,
};
use std::{sync::Arc, time::Duration};

use futures::{SinkExt, StreamExt};
use notify_rust::Notification;
#[cfg(target_os = "linux")]
use notify_rust::NotificationHandle;
use rand::{rngs::StdRng, Rng, SeedableRng};
use todel::models::{ClientPayload, Message, ServerPayload};
use todel::{ClientPayload, Message, ServerPayload, StatusType, User};
use tokio::sync::Mutex as AsyncMutex;
use tokio::time;
use tokio_tungstenite::{connect_async, tungstenite::Message as WsMessage};
use tui::style::{Color, Style};

use crate::models::{PilferMessage, SystemMessage};
use crate::models::{AppContext, PilferMessage, Response, SystemMessage};

pub async fn handle_gateway(
gateway_url: String,
messages: Arc<Mutex<Vec<(PilferMessage, Style)>>>,
focused: Arc<AtomicBool>,
#[cfg(target_os = "linux")] notification: Arc<Mutex<Option<NotificationHandle>>>,
name: String,
) {
// It's either this or excessive amounts of arcs and mutexes over AppContext.
#[allow(clippy::too_many_arguments)]
pub async fn handle_gateway(app: Arc<AppContext>, token: String) {
let rng = Arc::new(AsyncMutex::new(StdRng::from_entropy()));
let mut wait = 0;
loop {
if wait > 0 {
time::sleep(Duration::from_secs(wait)).await;
}

let socket = match connect_async(&gateway_url).await {
let socket = match connect_async(&app.gateway_url).await {
Ok((socket, _)) => socket,
Err(err) => {
if wait < 64 {
wait *= 2;
}
messages.lock().unwrap().push((
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: format!(
"Could not connect: {:?}, reconnecting in {}s (press Ctrl+C to exit)",
Expand All @@ -54,32 +45,64 @@ pub async fn handle_gateway(
let ping;
loop {
if let Some(Ok(WsMessage::Text(msg))) = rx.next().await {
if let Ok(ServerPayload::Hello {
heartbeat_interval, ..
}) = serde_json::from_str(&msg)
{
// Handle ping-pong loop
let rng = Arc::clone(&rng);
ping = tokio::spawn(async move {
time::sleep(Duration::from_secs(
rng.lock().await.gen_range(0..heartbeat_interval),
))
.await;
while let Ok(()) = tx
.send(WsMessage::Text(
serde_json::to_string(&ClientPayload::Ping).unwrap(),
))
.await
{
time::sleep(Duration::from_secs(heartbeat_interval)).await;
if let Ok(msg) = serde_json::from_str(&msg) {
match msg {
ServerPayload::Hello {
heartbeat_interval, ..
} => {
// Authenticate
if let Err(err) = tx
.send(WsMessage::Text(
serde_json::to_string(&ClientPayload::Authenticate(
token.clone(),
))
.unwrap(),
))
.await
{
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: format!("Could not authenticate: {:?}", err),
}),
Style::default().fg(Color::Red),
));
return;
}

// Handle ping-pong loop
let rng = Arc::clone(&rng);
ping = tokio::spawn(async move {
time::sleep(Duration::from_millis(
rng.lock().await.gen_range(0..heartbeat_interval),
))
.await;
while let Ok(()) = tx
.send(WsMessage::Text(
serde_json::to_string(&ClientPayload::Ping).unwrap(),
))
.await
{
time::sleep(Duration::from_millis(heartbeat_interval)).await;
}
});
break;
}
ServerPayload::RateLimit { wait } => {
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: format!("Rate limited, waiting {}s", wait / 1000),
}),
Style::default().fg(Color::Red),
));
time::sleep(Duration::from_millis(wait)).await;
}
});
break;
_ => continue,
}
}
}
}

messages.lock().unwrap().push((
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: "Connected to Pandemonium".to_string(),
}),
Expand All @@ -92,23 +115,83 @@ pub async fn handle_gateway(
WsMessage::Text(msg) => {
let msg: Message = match serde_json::from_str(&msg) {
Ok(ServerPayload::MessageCreate(msg)) => msg,
Ok(ServerPayload::Authenticated {
user,
users: online_users,
}) => {
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: "Authenticated with Pandemonium!".to_string(),
}),
Style::default().fg(Color::Green),
));
let mut users = app.users.lock().await;
users.insert(user.id, user);
users.extend(online_users.into_iter().map(|user| (user.id, user)));
continue;
}
Ok(ServerPayload::UserUpdate(user)) => {
if user.status.status_type != StatusType::Offline {
app.users.lock().await.insert(user.id, user);
}
continue;
}
Ok(ServerPayload::PresenceUpdate { user_id, status }) => {
let mut users = app.users.lock().await;

if status.status_type == StatusType::Offline {
users.remove(&user_id);
continue;
};

if let Some(user) = users.get_mut(&user_id) {
user.status = status;
} else {
let user = match app
.http_client
.get(format!("{}/users/{}", app.rest_url, user_id))
.send()
.await
.expect("Can not connect to Oprish")
.json::<Response<User>>()
.await
.unwrap()
{
Response::Success(user) => user,
Response::Error(err) => {
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: format!(
"Could not get user {}: {:?}",
user_id, err
),
}),
Style::default().fg(Color::Red),
));
continue;
}
};
users.insert(user.id, user);
}
continue;
}
_ => continue,
};
if !focused.load(std::sync::atomic::Ordering::Relaxed) {
if !app.focused.load(std::sync::atomic::Ordering::Relaxed) {
#[cfg(target_os = "linux")]
{
let mut notif = notification.lock().unwrap();
let mut notif = app.notification.lock().unwrap();
match notif.as_mut() {
Some(notif) => {
notif
.summary(&format!("New Pilfer message from {}", msg.author))
.body(&msg.content.to_string());
.body(&msg.message.content.to_string());
notif.update()
}
None => {
*notif = match Notification::new()
.summary(&format!("New Pilfer message from {}", msg.author))
.body(&msg.content)
.body(&msg.message.content)
.show()
{
Ok(notif) => Some(notif),
Expand All @@ -120,28 +203,33 @@ pub async fn handle_gateway(
#[cfg(not(target_os = "linux"))]
Notification::new()
.summary(&format!("New Pilfer message from {}", msg.author))
.body(&msg.content)
.body(&msg.message.content)
.show()
.ok();
}
// Highlight the message if your name got mentioned
let style = if msg.content.to_lowercase().contains(&name.to_lowercase()) {
let style = if msg
.message
.content
.to_lowercase()
.contains(&app.name.to_lowercase())
{
Style::default().fg(Color::Yellow)
} else {
Style::default()
};
// Add to the Pifler's context
messages
app.messages
.lock()
.unwrap()
.push((PilferMessage::Eludris(msg), style));
.push((PilferMessage::Eludris(Box::new(msg)), style));
}
WsMessage::Close(frame) => {
if let Some(frame) = frame {
if wait < 64 {
wait *= 2;
}
messages.lock().unwrap().push((
app.messages.lock().unwrap().push((
PilferMessage::System(SystemMessage {
content: format!("{}, retrying in {}s", frame.reason, wait),
}),
Expand Down
Loading
Loading