Advanced PMs

This commit is contained in:
Bommels05 2025-02-08 17:27:44 +01:00
parent 68de25838c
commit a8b3f7f871
8 changed files with 607 additions and 137 deletions

View File

@ -21,3 +21,4 @@ url = "2.5.4"
flume = "0.11.1" flume = "0.11.1"
error-stack = "0.5.0" error-stack = "0.5.0"
thiserror = "2.0.11" thiserror = "2.0.11"
chrono = { version = "0.4.39", features = ["serde"] }

View File

@ -18,6 +18,7 @@ url.workspace = true
lalrpop-util = "0.22.1" lalrpop-util = "0.22.1"
error-stack.workspace = true error-stack.workspace = true
flume.workspace = true flume.workspace = true
chrono.workspace = true
[build-dependencies] [build-dependencies]
lalrpop = "0.22.1" lalrpop = "0.22.1"

View File

@ -1,6 +1,6 @@
use banklib::config::{load_config, save_config}; use banklib::config::{load_config, save_config};
use banklib::extended::{Client, Credentials, State}; use banklib::extended::{Client, Credentials, MessageHistoryEntry, State};
use banklib::{AuthenticatedMessage, BankClient, BankError, BankResult, ClientAction, Response, ResponseMessage}; use banklib::{AuthenticatedMessage, BankClient, BankError, BankResult, ClientAction, MsgType, Response, ResponseMessage};
use clap::Parser; use clap::Parser;
use error_stack::{report, Report}; use error_stack::{report, Report};
use flume::Receiver; use flume::Receiver;
@ -269,6 +269,13 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
} else { } else {
handle_error(&result); handle_error(&result);
} }
} else {
if !ask_confirmation(rx, "Do you still want to add the account?").await {
client.config.accounts.accounts.remove(&credentials.username);
if client.config.accounts.default_account.eq(&credentials.username) {
client.config.accounts.default_account = String::new();
}
}
} }
} }
} }
@ -298,6 +305,9 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
let username: &str = split.next().or(Some("")).unwrap(); let username: &str = split.next().or(Some("")).unwrap();
if client.config.accounts.accounts.contains_key(username) { if client.config.accounts.accounts.contains_key(username) {
client.config.accounts.accounts.remove(username); client.config.accounts.accounts.remove(username);
if client.config.accounts.default_account.eq(username) {
client.config.accounts.default_account = String::new();
}
println!("Account removed"); println!("Account removed");
} else { } else {
println!("You need to enter a valid username") println!("You need to enter a valid username")
@ -479,37 +489,196 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
let result = client.send_pm(username.unwrap().into(), message).await; let result = client.send_pm(username.unwrap().into(), message).await;
handle_error(&result); handle_error(&result);
} else if username.is_none() && username.is_none() { } else if username.is_none() && username.is_none() {
let size = client.get_pm_size().await; let use_fake_inbox = if client.config.acknowledgements.use_fake_pm_inbox {
if let Ok(size) = size { client.config.general.use_fake_pm_inbox
info!("--- {} Private Message{} ---", size, if size == 1 { "" } else { "s" });
for i in 0..size {
let msg = client.get_pm_msg((i + 1) as i8).await;
if let Ok((id, msg)) = msg {
info!("{}. {}", id, msg);
} else { } else {
handle_error(&msg); let use_fake_inbox = ask_confirmation(rx, "To correctly record the first time these messages were seen the messages need to be deleted from the Server. This may prevent you from viewing these private messages using other clients. The messages will still be marked as unread in this client.").await;
if ask_confirmation(rx, "Do you want this answer to be saved?").await {
client.config.acknowledgements.use_fake_pm_inbox = true;
client.config.general.use_fake_pm_inbox = use_fake_inbox;
}
use_fake_inbox
};
if use_fake_inbox {
let result = client.update_new_pm_history().await;
if result.is_err() {
handle_error(&result);
return false;
} }
} }
info!("--- {} Private Message{} ---", size, if size == 1 { "" } else { "s" });
let new = client.get_new_pms().await;
if let Ok(new) = new {
let history = client.get_new_pm_history().await;
if let Ok(history) = history {
let size = new.len() + history.len();
info!("--- {} New Private Message{} ---", size, if size == 1 { "" } else { "s" });
for (id, message) in new {
info!("*{}. [?]{}", id, message);
}
let mut i = 1;
for msg in history.clone() {
info!("{}. [{}]{}: {}", i, msg.time.format("%M:%H %d.%m.%y"), msg.sender, msg.message);
i += 1;
}
info!("*: Message not saved in the history");
info!("--- {} New Private Message{} ---", size, if size == 1 { "" } else { "s" });
if ask_confirmation(rx, "Do you want to mark all messages as read?").await {
handle_error(&client.move_all_new_pms().await);
}
} else { } else {
handle_error(&size); handle_error(&history);
}
} else {
handle_error(&new);
} }
} else { } else {
error!("You need to enter a username and message or nothing to view your PMs") error!("You need to enter a username and message or nothing to view your PMs")
} }
} }
"chat" => {
let username = split.next().or(Some("")).unwrap();
if !username.is_empty() {
let result = client.get_pm_history(username.into()).await;
if let Ok(history) = result {
if client.config.general.use_fake_pm_inbox {
handle_error(&client.update_new_pm_history().await);
} else {
warn!("You do not have advanced storing of unread private messages enabled. These unread messages will not appear in the context here.")
}
let result = client.get_new_pm_history().await;
if let Ok(new_history) = result {
info!("--- Private Messages with {} ---", username);
info!("Type \"exit\" to exit");
for i in 0..client.config.general.chat_context_window {
if history.len() > i as usize {
let msg: MessageHistoryEntry = history.get(i as usize).unwrap().clone();
info!("[{}]{}: {}", msg.time.format("%H:%M:%S %d.%m.%y"), msg.sender, msg.message)
}
}
for msg in &new_history {
if msg.sender.eq_ignore_ascii_case(&username) {
info!("*[{}]{}: {}", msg.time.format("%H:%M %d.%m.%y"), msg.sender, msg.message)
}
}
if !new_history.is_empty() && ask_confirmation(rx, "Do you want to mark all unread messages as read now?").await {
handle_error(&client.move_all_new_pms().await);
}
let messages = client.client.receiver_msg().clone();
let actions = client.client.receiver_act().clone();
loop {
select! {
Ok(line) = rx.recv_async() => {
if line.eq_ignore_ascii_case("exit") {
info!("Successfully left the chat");
break;
} else {
handle_error(&client.send_pm(username.into(), line).await);
}
},
Ok(message) = messages.recv_async() => {
match message.message {
Response::DisplayMessage { message: _, msg_type } => match msg_type {
MsgType::PmReceived { sender, message: _ } => {
if sender.eq_ignore_ascii_case(&username) {
let result = client.move_newest_pm_to_history().await;
if let Ok(msg) = result {
info!("[{}]{}: {}", msg.time.format("%H:%M %d.%m.%y"), msg.sender, msg.message)
} else {
handle_error(&result);
}
} else {
info!("You got a pm from another user ({})", sender)
}
}
_ => {}
},
_ => {}
}
},
Ok(action) = actions.recv_async() => {
match action {
ClientAction::Connected => {
client.state.logged_in = false;
let result = client.login(client.state.last_credentials.clone().unwrap()).await;
if result.is_err() {
handle_error(&result);
break;
} else {
info!("Reconnected to the chat")
}
}
ClientAction::Disconnected => {}
}
},
}
}
} else {
handle_error(&result);
}
} else {
handle_error(&result);
}
} else {
error!("You need to enter a username")
}
}
"delete" => { "delete" => {
let argument = split.next().or(Some("")).unwrap(); let argument = split.next().or(Some("")).unwrap();
let id = split.next().or(Some("")).unwrap().parse(); let id = split.next().or(Some("")).unwrap();
if argument == "inbox" && id.is_ok() { if argument == "inbox" && !id.is_empty() {
let result = client.delete_inbox_msg(id.unwrap()).await; let parsed = id.parse();
handle_error(&result); if let Ok(i) = parsed {
} else if argument == "pm" && id.is_ok() { let result = client.delete_inbox_msg(i).await;
let result = client.delete_pm_msg(id.unwrap()).await;
handle_error(&result); handle_error(&result);
} else { } else {
println!("You need to specify inbox or pm and a message id"); println!("You need to specify inbox or pm and a message id");
} }
} else if argument == "pm" && !id.is_empty() {
if id.starts_with("*") {
let result = id.strip_prefix("*").unwrap().parse();
if result.is_err() {
println!("You need to specify inbox or pm and a message id");
return false;
}
let i: u8 = result.unwrap();
if client.config.general.use_pm_history {
let auto_import = if client.config.acknowledgements.auto_import_on_delete {
client.config.general.auto_import_on_delete
} else {
let auto_import = ask_confirmation(rx, "You have the message history enabled but specified to delete a message that is not in the new message history. Do you want to import all new messages into the non native history that may not be accessible for all clients or delete the message without saving it into the history?").await;
if ask_confirmation(rx, "Do you want this answer to be saved?").await {
client.config.acknowledgements.auto_import_on_delete = true;
client.config.general.auto_import_on_delete = auto_import;
}
auto_import
};
if auto_import {
let history = client.get_new_pm_history().await;
if let Ok(history) = history {
let i = i + (history.len() as u8) - 1;
handle_error(&client.update_new_pm_history().await);
handle_error(&client.move_from_new_pm_history(i).await);
}
} else {
handle_error(&client.delete_pm_msg(i, false).await);
}
} else {
handle_error(&client.delete_pm_msg(i, false).await);
}
} else {
let result = id.parse();
if result.is_err() {
println!("You need to specify inbox or pm and a message id");
return false;
}
let i: u8 = result.unwrap();
handle_error(&client.move_from_new_pm_history(i).await);
};
} else {
println!("You need to specify inbox or pm and a message id");
}
} }
"motd" => { "motd" => {
let motd = client.get_motd().await; let motd = client.get_motd().await;
@ -568,6 +737,7 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
println!("pay <username> <amount> - Pay someone Steam Coins"); println!("pay <username> <amount> - Pay someone Steam Coins");
println!("inbox - View your inbox"); println!("inbox - View your inbox");
println!("pm [destination] [message]- View your private messages or send one"); println!("pm [destination] [message]- View your private messages or send one");
println!("chat <username> - Chat with someone");
println!("delete <inbox|pm> <id> - Delete a message from your inbox or PMs"); println!("delete <inbox|pm> <id> - Delete a message from your inbox or PMs");
println!("motd - View the current server motd"); println!("motd - View the current server motd");
println!("logout - Logout of your current account"); println!("logout - Logout of your current account");
@ -584,8 +754,8 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
async fn handle_message(client: &mut Client, response: ResponseMessage) { async fn handle_message(client: &mut Client, response: ResponseMessage) {
match response.clone().message { match response.clone().message {
Response::DisplayMessage { message, id, .. } => match id.as_str() { Response::DisplayMessage { message, msg_type } => match msg_type {
"pay.received" => { MsgType::PaymentReceived { amount: _, sender: _ } => {
println!("{}", message); println!("{}", message);
if client.config.general.show_balance_after_payment { if client.config.general.show_balance_after_payment {
let balance = client.get_balance().await; let balance = client.get_balance().await;
@ -596,7 +766,7 @@ async fn handle_message(client: &mut Client, response: ResponseMessage) {
} }
} }
} }
"pm_inbox.received" => { MsgType::PmReceived { sender: _, message: _ } => {
println!("{}", message); println!("{}", message);
} }
_ => { print_error(&report!(BankError::UnexpectedMessage(response))); } _ => { print_error(&report!(BankError::UnexpectedMessage(response))); }

View File

@ -22,3 +22,4 @@ url.workspace = true
flume.workspace = true flume.workspace = true
error-stack.workspace = true error-stack.workspace = true
thiserror.workspace = true thiserror.workspace = true
chrono.workspace = true

View File

@ -10,6 +10,7 @@ pub struct Config {
pub accounts: AccountConfig, pub accounts: AccountConfig,
pub server: ServerConfig, pub server: ServerConfig,
pub general: GeneralConfig, pub general: GeneralConfig,
pub acknowledgements: AcknowledgementsConfig
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -33,6 +34,16 @@ pub struct GeneralConfig {
pub show_inbox_count_after_login: bool, pub show_inbox_count_after_login: bool,
pub show_pm_inbox_count_after_login: bool, pub show_pm_inbox_count_after_login: bool,
pub show_balance_after_payment: bool, pub show_balance_after_payment: bool,
pub use_pm_history: bool,
pub use_fake_pm_inbox: bool,
pub auto_import_on_delete: bool,
pub chat_context_window: u8,
}
#[derive(Serialize, Deserialize)]
pub struct AcknowledgementsConfig {
pub use_fake_pm_inbox: bool,
pub auto_import_on_delete: bool,
} }
pub fn load_config() -> Config { pub fn load_config() -> Config {
@ -42,12 +53,15 @@ pub fn load_config() -> Config {
let json: serde_json::Result<Config> = serde_json::from_str(content.unwrap().as_str()); let json: serde_json::Result<Config> = serde_json::from_str(content.unwrap().as_str());
if json.is_ok() { if json.is_ok() {
return json.unwrap(); return json.unwrap();
} else {
error!("Config invalid")
} }
} else { } else {
error!("Config invalid") error!("Config invalid")
} }
} }
Config {
let config = Config {
accounts: AccountConfig { accounts: AccountConfig {
default_account: "".to_string(), default_account: "".to_string(),
last_account: "".to_string(), last_account: "".to_string(),
@ -64,8 +78,18 @@ pub fn load_config() -> Config {
show_inbox_count_after_login: true, show_inbox_count_after_login: true,
show_pm_inbox_count_after_login: true, show_pm_inbox_count_after_login: true,
show_balance_after_payment: true, show_balance_after_payment: true,
use_pm_history: true,
use_fake_pm_inbox: true,
auto_import_on_delete: true,
chat_context_window: 10,
}, },
acknowledgements: AcknowledgementsConfig {
use_fake_pm_inbox: false,
auto_import_on_delete: false,
} }
};
save_config(&config);
config
} }
pub fn save_config(config: &Config) { pub fn save_config(config: &Config) {

View File

@ -1,8 +1,10 @@
#![cfg(feature = "extended")] #![cfg(feature = "extended")]
use chrono::{DateTime, Utc};
use error_stack::report; use error_stack::report;
use tracing::info; use serde::{Deserialize, Serialize};
use crate::{AuthenticatedMessage, BankClient, BankError, BankResult, Response, ResponseMessage}; use tracing::{info, warn};
use crate::{AuthenticatedMessage, BankClient, BankError, BankResult, MsgType, Response, ResponseMessage};
use crate::config::{save_config, Config}; use crate::config::{save_config, Config};
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
@ -29,10 +31,10 @@ macro_rules! check_message_id {
match &message.message { match &message.message {
Response::DisplayMessage { Response::DisplayMessage {
message: _, message: _,
id, msg_type,
.. ..
} => { } => {
matches!(id.as_str(), $pattern) matches!(msg_type, $pattern)
} }
_ => false, _ => false,
} }
@ -41,31 +43,31 @@ macro_rules! check_message_id {
}}; }};
} }
fn display_message(message: Response) -> (String, String, u32) { fn display_message(message: Response) -> (String, MsgType) {
match message { match message {
Response::DisplayMessage { message, id, value, .. } => return (message, id, value), Response::DisplayMessage { message, msg_type } => return (message, msg_type),
_ => panic!("Unexpected message type"), _ => panic!("Unexpected message type"),
} }
} }
impl Client { impl Client {
pub async fn logout(&mut self) -> BankResult<(), BankError> { pub async fn logout(&mut self) -> BankResult<(), BankError> {
let (message, id, _) = display_message( let (message, msg_type) = display_message(
self.client self.client
.send_authenticated( .send_authenticated(
AuthenticatedMessage::Logout, AuthenticatedMessage::Logout,
check_message_id!( check_message_id!(
"logout.success" | "logout.fail.notloggedin" | "logout.fail.credentials" MsgType::LogoutSuccess | MsgType::LogoutFailNotLoggedIn | MsgType::LogoutFailCredentials
), ),
) )
.await?, .await?,
); );
info!("{}", message); info!("{}", message);
match id.as_str() { match msg_type {
"logout.success" | "logout.fail.notloggedin" => { MsgType::LogoutSuccess | MsgType::LogoutFailNotLoggedIn => {
self.state.logged_in = false; self.state.logged_in = false;
} }
"logout.fail.credentials" => return Err(report!(BankError::AuthenticationError)), MsgType::LogoutFailCredentials => return Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
Ok(()) Ok(())
@ -76,51 +78,45 @@ impl Client {
self.logout().await?; self.logout().await?;
} }
self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?; self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?;
let (message, id, value) = display_message( let (message, msg_type) = display_message(
self.client self.client.send_authenticated(AuthenticatedMessage::Authenticate, check_message_id!(MsgType::AuthSuccess { new_id: _ } | MsgType::AuthFailCredentials)).await?,
.send_authenticated(
AuthenticatedMessage::Authenticate,
check_message_id!("auth.success" | "auth.fail.credentials"),
)
.await?,
); );
info!("{}", message); info!("{}", message);
match id.as_str() { match msg_type {
"auth.success" => { MsgType::AuthSuccess { new_id } => {
self.state.last_credentials = Some(credentials.clone()); self.state.last_credentials = Some(credentials.clone());
self.config.accounts.last_account = credentials.username.clone(); self.config.accounts.last_account = credentials.username.clone();
save_config(&self.config); save_config(&self.config);
self.state.logged_in = true; self.state.logged_in = true;
info!("(Client Id: {})", value); info!("(Client Id: {})", new_id);
if self.config.general.show_motd_after_login { if self.config.general.show_motd_after_login {
let motd = self.get_motd().await; let motd = self.get_motd().await;
if let Ok(motd) = motd { if let Ok(motd) = motd {
info!("--- {} ---", motd); info!("--- {} ---", motd);
} else {
return Err(motd.err().unwrap());
} }
} }
if self.config.general.show_inbox_count_after_login { if self.config.general.show_inbox_count_after_login {
let size = self.get_inbox_size().await; let size = self.get_inbox_size().await;
if let Ok(size) = size { if let Ok(size) = size {
info!( info!("You have {} unread message{}", size, if size == 1 { "" } else { "s" });
"You have {} unread message{}", } else {
size, return Err(size.err().unwrap());
if size == 1 { "" } else { "s" }
);
} }
} }
if self.config.general.show_pm_inbox_count_after_login { if self.config.general.show_pm_inbox_count_after_login {
let size = self.get_pm_size().await; let size = self.get_real_new_pm_count().await;
if let Ok(size) = size { if let Ok((new, unread)) = size {
info!( let size = new + unread;
"You have {} unread private message{}", info!("You have a total of {} ({} new) unread private message{}", size, new, if size == 1 { "" } else { "s" });
size, } else {
if size == 1 { "" } else { "s" } return Err(size.err().unwrap());
);
} }
} }
} }
"auth.fail.credentials" => return Err(report!(BankError::AuthenticationError)), MsgType::AuthFailCredentials => return Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
Ok(()) Ok(())
@ -128,11 +124,11 @@ impl Client {
pub async fn register(&mut self, credentials: Credentials) -> BankResult<(), BankError> { pub async fn register(&mut self, credentials: Credentials) -> BankResult<(), BankError> {
self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?; self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?;
let (_, id, _) = display_message(self.client.send_authenticated( let (_, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Register, check_message_id!("register.success" | "register.fail.usernameTaken")).await?); AuthenticatedMessage::Register, check_message_id!(MsgType::RegisterSuccess | MsgType::RegisterFailUsernameTaken)).await?);
match id.as_str() { match msg_type {
"register.success" => Ok(()), MsgType::RegisterSuccess => Ok(()),
"register.fail.usernameTaken" => Err(report!(BankError::AuthenticationError)), MsgType::RegisterFailUsernameTaken => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
@ -154,17 +150,17 @@ impl Client {
} }
pub async fn get_inbox_size(&mut self) -> BankResult<u8, BankError> { pub async fn get_inbox_size(&mut self) -> BankResult<u8, BankError> {
let (_, id, value) = display_message( let (_, msg_type) = display_message(
self.client self.client
.send_authenticated( .send_authenticated(
AuthenticatedMessage::GetInbox { message_id: -1 }, AuthenticatedMessage::GetInbox { message_id: -1 },
check_message_id!("message_count" | "inbox.fail.credentials"), check_message_id!(MsgType::InboxMessageCount { message_count: _ } | MsgType::InboxFailCredentials),
) )
.await?, .await?,
); );
match id.as_str() { match msg_type {
"message_count" => Ok(value as u8), MsgType::InboxMessageCount { message_count } => Ok(message_count as u8),
"inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::InboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
@ -173,17 +169,17 @@ impl Client {
let message = self.client.send_authenticated(AuthenticatedMessage::GetInbox { message_id: id }, |response| { let message = self.client.send_authenticated(AuthenticatedMessage::GetInbox { message_id: id }, |response| {
match &response.message { match &response.message {
Response::Inbox { .. } => true, Response::Inbox { .. } => true,
Response::DisplayMessage { message: _, id, .. } => { Response::DisplayMessage { message: _, msg_type } => {
id.as_str().eq("inbox.fail.credentials") msg_type.eq(&MsgType::InboxFailCredentials)
} }
_ => false, _ => false,
} }
}).await?; }).await?;
match message { match message {
Response::Inbox { message_id, message } => Ok((message_id, message)), Response::Inbox { message_id, message } => Ok((message_id, message)),
Response::DisplayMessage { message: _, id, .. } => { Response::DisplayMessage { message: _, msg_type } => {
match id.as_str() { match msg_type {
"inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::InboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
@ -192,27 +188,27 @@ impl Client {
} }
pub async fn delete_inbox_msg(&mut self, message_id: u8) -> BankResult<(), BankError> { pub async fn delete_inbox_msg(&mut self, message_id: u8) -> BankResult<(), BankError> {
let (message, id, _) = display_message(self.client.send_authenticated( let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::ReadInbox { message_id: message_id as i8 }, check_message_id!("read_inbox.success" | "read_inbox.fail.credentials")).await?); AuthenticatedMessage::ReadInbox { message_id: message_id as i8 }, check_message_id!(MsgType::DeleteInboxSuccess | MsgType::DeleteInboxFailCredentials)).await?);
info!("{}", message); info!("{}", message);
match id.as_str() { match msg_type {
"read_inbox.success" => Ok(()), MsgType::DeleteInboxSuccess => Ok(()),
"read_inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::DeleteInboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
pub async fn get_pm_size(&mut self) -> BankResult<u8, BankError> { pub async fn get_pm_size(&mut self) -> BankResult<u8, BankError> {
let (_, id, value) = display_message( let (_, msg_type) = display_message(
self.client self.client
.send_authenticated( .send_authenticated(
AuthenticatedMessage::GetPmInbox { message_id: -1 }, AuthenticatedMessage::GetPmInbox { message_id: -1 },
check_message_id!("pm_message_count" | "pm.fail.credentials"), check_message_id!(MsgType::PmMessageCount { message_count: _ } | MsgType::PmFailCredentials),
) )
.await?); .await?);
match id.as_str() { match msg_type {
"pm_message_count" => Ok(value as u8), MsgType::PmMessageCount { message_count } => Ok(message_count as u8),
"pm.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::PmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
@ -221,17 +217,17 @@ impl Client {
let message = self.client.send_authenticated(AuthenticatedMessage::GetPmInbox { message_id: id }, |response| { let message = self.client.send_authenticated(AuthenticatedMessage::GetPmInbox { message_id: id }, |response| {
match &response.message { match &response.message {
Response::PmInbox { .. } => true, Response::PmInbox { .. } => true,
Response::DisplayMessage { message: _, id, .. } => { Response::DisplayMessage { message: _, msg_type } => {
id.as_str().eq("pm.fail.credentials") msg_type.eq(&MsgType::PmFailCredentials)
} }
_ => false, _ => false,
} }
}).await?; }).await?;
match message { match message {
Response::PmInbox { message_id, message } => Ok((message_id, message)), Response::PmInbox { message_id, message } => Ok((message_id, message)),
Response::DisplayMessage { message: _, id, .. } => { Response::DisplayMessage { message: _, msg_type } => {
match id.as_str() { match msg_type {
"pm.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::PmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
@ -239,53 +235,232 @@ impl Client {
} }
} }
pub async fn delete_pm_msg(&mut self, message_id: u8) -> BankResult<(), BankError> { pub async fn delete_pm_msg(&mut self, message_id: u8, silent: bool) -> BankResult<(), BankError> {
let (message, id, _) = display_message(self.client.send_authenticated( let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::ReadPmInbox { message_id: message_id as i8 }, check_message_id!("read_pm.success" | "read_pm.fail.credentials")).await?); AuthenticatedMessage::ReadPmInbox { message_id: message_id as i8 }, check_message_id!(MsgType::DeletePmSuccess | MsgType::DeletePmFailCredentials)).await?);
if !silent {
info!("{}", message); info!("{}", message);
match id.as_str() { }
"read_pm.success" => Ok(()), match msg_type {
"read_pm.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::DeletePmSuccess => Ok(()),
MsgType::DeletePmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
pub async fn get_balance(&mut self) -> BankResult<u32, BankError> { pub async fn get_balance(&mut self) -> BankResult<u32, BankError> {
let (_, id, value) = display_message(self.client.send_authenticated( let (_, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::GetBalance, check_message_id!("balance.success" | "balance.fail.credentials")).await?); AuthenticatedMessage::GetBalance, check_message_id!(MsgType::BalanceSuccess { balance: _ } | MsgType::BalanceFailCredentials)).await?);
match id.as_str() { match msg_type {
"balance.success" => Ok(value), MsgType::BalanceSuccess { balance } => Ok(balance),
"balance.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::BalanceFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
pub async fn pay(&mut self, destination: String, amount: u32) -> BankResult<(), BankError> { pub async fn pay(&mut self, destination: String, amount: u32) -> BankResult<(), BankError> {
let (message, id, _) = display_message(self.client.send_authenticated( let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Pay { destination, amount }, check_message_id!("pay.success" | "pay.fail.unknown_error" | AuthenticatedMessage::Pay { destination, amount }, check_message_id!(MsgType::PaySuccess | MsgType::PayFailInternalError |
"pay.fail.not_enough_money" | "pay.fail.credentials" | "pay.fail.unknown_dest" | "pay.fail.orig_user_unknown" | "pay.fail.negative_amount")).await?); MsgType::PayFailNotEnoughMoney | MsgType::PayFailCredentials | MsgType::PayFailUnknownDestination |
MsgType::PayFailUnknownUsername | MsgType::PayFailNegativeAmount)).await?);
info!("{}", message); info!("{}", message);
match id.as_str() { match msg_type {
"pay.success" => Ok(()), MsgType::PaySuccess => Ok(()),
"pay.fail.not_enough_money" => Err(report!(BankError::NotEnoughMoney)), MsgType::PayFailNotEnoughMoney => Err(report!(BankError::NotEnoughMoney)),
"pay.fail.unknown_dest" => Err(report!(BankError::DestinationUnknown)), MsgType::PayFailUnknownDestination => Err(report!(BankError::DestinationUnknown)),
"pay.fail.credentials" => Err(report!(BankError::AuthenticationError)), MsgType::PayFailCredentials => Err(report!(BankError::AuthenticationError)),
"pay.fail.orig_user_unknown" => Err(report!(BankError::AuthenticationError)), MsgType::PayFailUnknownUsername => Err(report!(BankError::AuthenticationError)),
"pay.fail.negative_amount" => Err(report!(BankError::InternalError)), MsgType::PayFailNegativeAmount => Err(report!(BankError::InternalError)),
"pay.fail.unknown_error" => Err(report!(BankError::InternalError)), MsgType::PayFailInternalError => Err(report!(BankError::InternalError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
pub async fn send_pm(&mut self, destination: String, message: String) -> BankResult<(), BankError> { pub async fn send_pm(&mut self, destination: String, message: String) -> BankResult<(), BankError> {
let (message, id, _) = display_message(self.client.send_authenticated( let (server_message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::SendPm { destination, message }, check_message_id!("pm_inbox.send.success" | "pm_inbox.dest.unknown" | "pm_inbox.fail.credentials")).await?); AuthenticatedMessage::SendPm { destination: destination.clone(), message: message.clone() }, check_message_id!(MsgType::PmSendSuccess | MsgType::PMFailDestinationUnknown | MsgType::PmFailCredentials)).await?);
info!("{}", message); info!("{}", server_message);
match id.as_str() { match msg_type {
"pm_inbox.send.success" => Ok(()), MsgType::PmSendSuccess => {
"pm_inbox.dest.unknown" => Err(report!(BankError::DestinationUnknown)), if self.config.general.use_pm_history {
"pm_inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)), let current_username = self.state.last_credentials.clone().unwrap().username;
self.add_to_pm_history(destination, MessageHistoryEntry { message, sender: current_username, time: Utc::now() }).await?;
}
Ok(())
},
MsgType::PMFailDestinationUnknown => Err(report!(BankError::DestinationUnknown)),
MsgType::PmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"), _ => panic!("Unexpected message"),
} }
} }
pub async fn set_custom_data(&mut self, key: String, data: String) -> BankResult<(), BankError> {
let response = self.client.send_authenticated(AuthenticatedMessage::CustomDataInsert { key, data }, |msg| {
match &msg.message {
Response::CustomData { response_type, .. } => {
response_type.eq("insert")
}
_ => false
}
}).await?;
match response {
Response::CustomData { data, .. } => {
if data.eq("success") {
Ok(())
} else {
Err(report!(BankError::InternalError))
}
},
_ => panic!("Unexpected message type"),
}
}
pub async fn get_custom_data(&mut self, key: String) -> BankResult<String, BankError> {
let response = self.client.send_authenticated(AuthenticatedMessage::CustomDataGet { key }, |msg| {
match &msg.message {
Response::CustomData { response_type, .. } => {
response_type.eq("get")
}
_ => false
}
}).await?;
match response {
Response::CustomData { data, .. } => Ok(data),
_ => panic!("Unexpected message type"),
}
}
/*More extended*/
pub async fn get_pm_history(&mut self, username: String) -> BankResult<Vec<MessageHistoryEntry>, BankError> {
let data = self.get_custom_data(format!("pm_history.{}", username)).await?;
if data.is_empty() {
Ok(Vec::new())
} else {
let history: BankResult<Vec<MessageHistoryEntry>, BankError> = serde_json::from_str(&data).map_err(|_| report!(BankError::InvalidData));
history
}
}
pub async fn set_pm_history(&mut self, username: String, history: Vec<MessageHistoryEntry>) -> BankResult<(), BankError> {
self.set_custom_data(format!("pm_history.{}", username), serde_json::to_string(&history).map_err(|_| report!(BankError::InvalidData))?).await
}
pub async fn add_to_pm_history(&mut self, username: String, entry: MessageHistoryEntry) -> BankResult<(), BankError> {
let mut history = self.get_pm_history(username.clone()).await?.clone();
history.push(entry);
self.set_pm_history(username, history).await
}
pub async fn get_new_pms(&mut self) -> BankResult<Vec<(u8, String)>, BankError> {
let size = self.get_pm_size().await?;
let mut pms = Vec::new();
for i in 0..size {
pms.push(self.get_pm_msg((i + 1) as i8).await?);
}
Ok(pms)
}
pub async fn delete_new_pms(&mut self) -> BankResult<(), BankError> {
let size = self.get_pm_size().await?;
for i in 0..size {
self.delete_pm_msg(i + 1, true).await?;
}
Ok(())
}
pub async fn update_new_pm_history(&mut self) -> BankResult<(), BankError> {
let pms = self.get_new_pms().await?;
let mut history: Vec<MessageHistoryEntry> = self.get_new_pm_history().await?.clone();
for (_, message) in pms {
history.push(Self::entry_from_new_pm(message))
}
self.set_pm_history("new".into(), history).await?;
let result = self.delete_new_pms().await;
if result.is_err() {
warn!("Error moving new PMs from native to custom data - PMs are now possibly duplicate");
return result;
}
info!("Successfully updated the list of unread PMs");
Ok(())
}
fn entry_from_new_pm(message: String) -> MessageHistoryEntry {
let split: Vec<&str> = message.split(':').collect();
let (sender, message) = if split.len() <= 1 {
warn!("Cannot determine sender of PM");
("Unknown".into(), message)
} else {
(split[0].into(), split[1].trim_start().into())
};
MessageHistoryEntry { message, sender, time: Utc::now() }
}
pub async fn get_new_pm_history(&mut self) -> BankResult<Vec<MessageHistoryEntry>, BankError> {
self.get_pm_history("new".into()).await
}
pub async fn delete_new_pm_history(&mut self) -> BankResult<(), BankError> {
self.set_pm_history("new".into(), Vec::new()).await
}
pub async fn move_from_new_pm_history(&mut self, id: u8) -> BankResult<(), BankError> {
let mut history = self.get_new_pm_history().await?;
let entry = history.remove(id as usize);
self.add_to_pm_history(entry.sender.clone(), entry).await?;
self.set_pm_history("new".into(), history).await
}
pub async fn move_newest_pm_to_history(&mut self) -> BankResult<MessageHistoryEntry, BankError> {
let pms = self.get_new_pms().await?;
let entry = Self::entry_from_new_pm(pms.iter().last().unwrap().1.clone());
self.add_to_pm_history(entry.sender.clone(), entry.clone()).await?;
let result = self.delete_pm_msg(pms.len() as u8, true).await;
if result.is_err() {
warn!("Error moving new PM to history - PMs are now possibly duplicate");
}
Ok(entry)
}
pub async fn move_all_new_pms(&mut self) -> BankResult<(), BankError> {
if self.config.general.use_fake_pm_inbox {
if self.config.general.use_pm_history {
let history = self.get_new_pm_history().await?;
for msg in history {
let result = self.add_to_pm_history(msg.sender.clone(), msg).await;
if result.is_err() {
warn!("Error saving pms to history - PMs are now possible duplicate");
return result;
}
}
}
self.delete_new_pm_history().await?;
Ok(())
} else {
if !self.config.general.use_pm_history {
/*if !ask_confirmation(rx, */warn!("You have the PM-History enabled but have disabled the history for new PMs. New PMs will now just be deleted and not saved into the history. Are you sure you want to continue?")/*.await {*/
/* return Ok(());
}*/
}
self.delete_new_pms().await?;
self.delete_new_pm_history().await?;
Ok(())
}
}
pub async fn get_real_new_pm_count(&mut self) -> BankResult<(u8, u8), BankError> {
let i = self.get_pm_size().await?;
let i2 = self.get_new_pm_history().await?.len();
Ok((i, i2 as u8))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageHistoryEntry {
pub message: String,
pub sender: String,
pub time: DateTime<Utc>,
} }

View File

@ -43,7 +43,9 @@ pub enum BankError {
#[error("You don't have enough Steam Coins")] #[error("You don't have enough Steam Coins")]
NotEnoughMoney, NotEnoughMoney,
#[error("Unknown unexpected message received {_0:?}")] #[error("Unknown unexpected message received {_0:?}")]
UnexpectedMessage(ResponseMessage) UnexpectedMessage(ResponseMessage),
#[error("Error handling Data")]
InvalidData,
} }
pub type BankResult<T, E = BankError> = error_stack::Result<T, E>; pub type BankResult<T, E = BankError> = error_stack::Result<T, E>;
@ -191,20 +193,27 @@ pub enum AuthenticatedMessage {
message: String, message: String,
}, },
Motd, Motd,
CustomDataInsert {
#[serde(rename = "value3")]
key: String,
#[serde(rename = "value4")]
data: String,
},
CustomDataGet {
#[serde(rename = "value3")]
key: String,
},
} }
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[serde(tag = "action", rename_all = "camelCase")] #[serde(tag = "action", rename_all = "snake_case")]
pub enum Response { pub enum Response {
#[serde(rename = "displayMessage")]
DisplayMessage { DisplayMessage {
#[serde(rename = "value1")] #[serde(rename = "value1")]
message: String, message: String,
#[serde(rename = "value2")] #[serde(flatten)]
id: String, msg_type: MsgType,
#[serde(rename = "value3", default = "default_u32")]
value: u32,
#[serde(rename = "value4", default = "String::new")]
value2: String,
}, },
Inbox { Inbox {
#[serde(rename = "value1")] #[serde(rename = "value1")]
@ -212,7 +221,6 @@ pub enum Response {
#[serde(rename = "value2")] #[serde(rename = "value2")]
message: String, message: String,
}, },
#[serde(rename = "pm_inbox")]
PmInbox { PmInbox {
#[serde(rename = "value1")] #[serde(rename = "value1")]
message_id: u8, message_id: u8,
@ -223,6 +231,95 @@ pub enum Response {
#[serde(rename = "value1")] #[serde(rename = "value1")]
motd: String, motd: String,
}, },
CustomData {
#[serde(rename = "value1")]
response_type: String,
#[serde(rename = "value2")]
data: String,
}
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(tag = "value2")]
pub enum MsgType {
#[serde(rename = "auth.success")]
AuthSuccess {
#[serde(rename = "value3")]
new_id: u32,
},
#[serde(rename = "auth.fail.credentials")]
AuthFailCredentials,
#[serde(rename = "register.success")]
RegisterSuccess,
#[serde(rename = "register.fail.usernameTaken")]
RegisterFailUsernameTaken,
#[serde(rename = "logout.success")]
LogoutSuccess,
#[serde(rename = "logout.fail.credentials")]
LogoutFailCredentials,
#[serde(rename = "logout.fail.notloggedin")]
LogoutFailNotLoggedIn,
#[serde(rename = "pay.success")]
PaySuccess,
#[serde(rename = "pay.fail.negative_amount")]
PayFailNegativeAmount,
#[serde(rename = "pay.fail.orig_user_unknown")]
PayFailUnknownUsername,
#[serde(rename = "pay.fail.unknown_dest")]
PayFailUnknownDestination,
#[serde(rename = "pay.fail.credentials")]
PayFailCredentials,
#[serde(rename = "pay.fail.not_enough_money")]
PayFailNotEnoughMoney,
#[serde(rename = "pay.fail.unknown_error")]
PayFailInternalError,
#[serde(rename = "inbox.fail.credentials")]
InboxFailCredentials,
#[serde(rename = "pm.fail.credentials")]
PmFailCredentials,
#[serde(rename = "balance.fail.credentials")]
BalanceFailCredentials,
#[serde(rename = "balance.success")]
BalanceSuccess {
#[serde(rename = "value3")]
balance: u32,
},
#[serde(rename = "pm_inbox.send.success")]
PmSendSuccess,
#[serde(rename = "pm_inbox.dest.unknown")]
PMFailDestinationUnknown,
#[serde(rename = "read_inbox.success")]
DeleteInboxSuccess,
#[serde(rename = "read_inbox.fail.credentials")]
DeleteInboxFailCredentials,
#[serde(rename = "read_pm.success")]
DeletePmSuccess,
#[serde(rename = "read_pm.fail.credentials")]
DeletePmFailCredentials,
#[serde(rename = "pay.received")]
PaymentReceived {
#[serde(rename = "value3")]
amount: u32,
#[serde(rename = "value4")]
sender: String,
},
#[serde(rename = "pm_inbox.received")]
PmReceived {
#[serde(rename = "value3")]
sender: String,
#[serde(rename = "value4")]
message: String,
},
#[serde(rename = "message_count")]
InboxMessageCount {
#[serde(rename = "value3")]
message_count: u32,
},
#[serde(rename = "pm_message_count")]
PmMessageCount {
#[serde(rename = "value3")]
message_count: u32,
},
} }
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
@ -237,6 +334,7 @@ fn default_i32() -> i32 {
-1 -1
} }
#[allow(dead_code)]
fn default_u32() -> u32 { fn default_u32() -> u32 {
0 0
} }
@ -252,10 +350,10 @@ impl ezsockets::ClientExt for Handler {
serde_json::from_str(text.as_str()).expect("Error decoding message"); serde_json::from_str(text.as_str()).expect("Error decoding message");
if message.id == self.id || message.id == -1 { if message.id == self.id || message.id == -1 {
match &message.message { match &message.message {
Response::DisplayMessage { message: _, id, value, .. } => { Response::DisplayMessage { message: _, msg_type } => {
match id.as_str() { match msg_type {
"auth.success" => { MsgType::AuthSuccess { new_id } => {
self.id = value.clone() as i32; self.id = new_id.clone() as i32;
} }
_ => {} _ => {}
} }

View File