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

@ -1,6 +1,6 @@
[workspace]
resolver = "3"
members = [ "bankcli","banklib"]
members = ["bankcli","banklib"]
[workspace.package]
version = "0.0.1"
@ -21,3 +21,4 @@ url = "2.5.4"
flume = "0.11.1"
error-stack = "0.5.0"
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"
error-stack.workspace = true
flume.workspace = true
chrono.workspace = true
[build-dependencies]
lalrpop = "0.22.1"

View File

@ -1,6 +1,6 @@
use banklib::config::{load_config, save_config};
use banklib::extended::{Client, Credentials, State};
use banklib::{AuthenticatedMessage, BankClient, BankError, BankResult, ClientAction, Response, ResponseMessage};
use banklib::extended::{Client, Credentials, MessageHistoryEntry, State};
use banklib::{AuthenticatedMessage, BankClient, BankError, BankResult, ClientAction, MsgType, Response, ResponseMessage};
use clap::Parser;
use error_stack::{report, Report};
use flume::Receiver;
@ -269,6 +269,13 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
} else {
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();
if client.config.accounts.accounts.contains_key(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");
} else {
println!("You need to enter a valid username")
@ -479,34 +489,193 @@ async fn handle_line(client: &mut Client, rx: &Receiver<String>, line: String) -
let result = client.send_pm(username.unwrap().into(), message).await;
handle_error(&result);
} else if username.is_none() && username.is_none() {
let size = client.get_pm_size().await;
if let Ok(size) = size {
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 {
handle_error(&msg);
}
}
info!("--- {} Private Message{} ---", size, if size == 1 { "" } else { "s" });
let use_fake_inbox = if client.config.acknowledgements.use_fake_pm_inbox {
client.config.general.use_fake_pm_inbox
} else {
handle_error(&size);
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;
}
}
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 {
handle_error(&history);
}
} else {
handle_error(&new);
}
} else {
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" => {
let argument = split.next().or(Some("")).unwrap();
let id = split.next().or(Some("")).unwrap().parse();
if argument == "inbox" && id.is_ok() {
let result = client.delete_inbox_msg(id.unwrap()).await;
handle_error(&result);
} else if argument == "pm" && id.is_ok() {
let result = client.delete_pm_msg(id.unwrap()).await;
handle_error(&result);
let id = split.next().or(Some("")).unwrap();
if argument == "inbox" && !id.is_empty() {
let parsed = id.parse();
if let Ok(i) = parsed {
let result = client.delete_inbox_msg(i).await;
handle_error(&result);
} else {
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");
}
@ -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!("inbox - View your inbox");
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!("motd - View the current server motd");
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) {
match response.clone().message {
Response::DisplayMessage { message, id, .. } => match id.as_str() {
"pay.received" => {
Response::DisplayMessage { message, msg_type } => match msg_type {
MsgType::PaymentReceived { amount: _, sender: _ } => {
println!("{}", message);
if client.config.general.show_balance_after_payment {
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);
}
_ => { print_error(&report!(BankError::UnexpectedMessage(response))); }

View File

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

View File

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

View File

@ -1,8 +1,10 @@
#![cfg(feature = "extended")]
use chrono::{DateTime, Utc};
use error_stack::report;
use tracing::info;
use crate::{AuthenticatedMessage, BankClient, BankError, BankResult, Response, ResponseMessage};
use serde::{Deserialize, Serialize};
use tracing::{info, warn};
use crate::{AuthenticatedMessage, BankClient, BankError, BankResult, MsgType, Response, ResponseMessage};
use crate::config::{save_config, Config};
#[derive(Clone, Eq, PartialEq)]
@ -29,10 +31,10 @@ macro_rules! check_message_id {
match &message.message {
Response::DisplayMessage {
message: _,
id,
msg_type,
..
} => {
matches!(id.as_str(), $pattern)
matches!(msg_type, $pattern)
}
_ => 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 {
Response::DisplayMessage { message, id, value, .. } => return (message, id, value),
Response::DisplayMessage { message, msg_type } => return (message, msg_type),
_ => panic!("Unexpected message type"),
}
}
impl Client {
pub async fn logout(&mut self) -> BankResult<(), BankError> {
let (message, id, _) = display_message(
let (message, msg_type) = display_message(
self.client
.send_authenticated(
AuthenticatedMessage::Logout,
check_message_id!(
"logout.success" | "logout.fail.notloggedin" | "logout.fail.credentials"
MsgType::LogoutSuccess | MsgType::LogoutFailNotLoggedIn | MsgType::LogoutFailCredentials
),
)
.await?,
);
info!("{}", message);
match id.as_str() {
"logout.success" | "logout.fail.notloggedin" => {
match msg_type {
MsgType::LogoutSuccess | MsgType::LogoutFailNotLoggedIn => {
self.state.logged_in = false;
}
"logout.fail.credentials" => return Err(report!(BankError::AuthenticationError)),
MsgType::LogoutFailCredentials => return Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
Ok(())
@ -76,51 +78,45 @@ impl Client {
self.logout().await?;
}
self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?;
let (message, id, value) = display_message(
self.client
.send_authenticated(
AuthenticatedMessage::Authenticate,
check_message_id!("auth.success" | "auth.fail.credentials"),
)
.await?,
let (message, msg_type) = display_message(
self.client.send_authenticated(AuthenticatedMessage::Authenticate, check_message_id!(MsgType::AuthSuccess { new_id: _ } | MsgType::AuthFailCredentials)).await?,
);
info!("{}", message);
match id.as_str() {
"auth.success" => {
match msg_type {
MsgType::AuthSuccess { new_id } => {
self.state.last_credentials = Some(credentials.clone());
self.config.accounts.last_account = credentials.username.clone();
save_config(&self.config);
self.state.logged_in = true;
info!("(Client Id: {})", value);
info!("(Client Id: {})", new_id);
if self.config.general.show_motd_after_login {
let motd = self.get_motd().await;
if let Ok(motd) = motd {
info!("--- {} ---", motd);
} else {
return Err(motd.err().unwrap());
}
}
if self.config.general.show_inbox_count_after_login {
let size = self.get_inbox_size().await;
if let Ok(size) = size {
info!(
"You have {} unread message{}",
size,
if size == 1 { "" } else { "s" }
);
info!("You have {} unread message{}", size, if size == 1 { "" } else { "s" });
} else {
return Err(size.err().unwrap());
}
}
if self.config.general.show_pm_inbox_count_after_login {
let size = self.get_pm_size().await;
if let Ok(size) = size {
info!(
"You have {} unread private message{}",
size,
if size == 1 { "" } else { "s" }
);
let size = self.get_real_new_pm_count().await;
if let Ok((new, unread)) = size {
let size = new + unread;
info!("You have a total of {} ({} new) unread private message{}", size, new, if size == 1 { "" } else { "s" });
} else {
return Err(size.err().unwrap());
}
}
}
"auth.fail.credentials" => return Err(report!(BankError::AuthenticationError)),
MsgType::AuthFailCredentials => return Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
Ok(())
@ -128,11 +124,11 @@ impl Client {
pub async fn register(&mut self, credentials: Credentials) -> BankResult<(), BankError> {
self.client.set_credentials(credentials.username.clone(), credentials.password.clone())?;
let (_, id, _) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Register, check_message_id!("register.success" | "register.fail.usernameTaken")).await?);
match id.as_str() {
"register.success" => Ok(()),
"register.fail.usernameTaken" => Err(report!(BankError::AuthenticationError)),
let (_, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Register, check_message_id!(MsgType::RegisterSuccess | MsgType::RegisterFailUsernameTaken)).await?);
match msg_type {
MsgType::RegisterSuccess => Ok(()),
MsgType::RegisterFailUsernameTaken => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
@ -154,17 +150,17 @@ impl Client {
}
pub async fn get_inbox_size(&mut self) -> BankResult<u8, BankError> {
let (_, id, value) = display_message(
let (_, msg_type) = display_message(
self.client
.send_authenticated(
AuthenticatedMessage::GetInbox { message_id: -1 },
check_message_id!("message_count" | "inbox.fail.credentials"),
check_message_id!(MsgType::InboxMessageCount { message_count: _ } | MsgType::InboxFailCredentials),
)
.await?,
);
match id.as_str() {
"message_count" => Ok(value as u8),
"inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)),
match msg_type {
MsgType::InboxMessageCount { message_count } => Ok(message_count as u8),
MsgType::InboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
@ -173,17 +169,17 @@ impl Client {
let message = self.client.send_authenticated(AuthenticatedMessage::GetInbox { message_id: id }, |response| {
match &response.message {
Response::Inbox { .. } => true,
Response::DisplayMessage { message: _, id, .. } => {
id.as_str().eq("inbox.fail.credentials")
Response::DisplayMessage { message: _, msg_type } => {
msg_type.eq(&MsgType::InboxFailCredentials)
}
_ => false,
}
}).await?;
match message {
Response::Inbox { message_id, message } => Ok((message_id, message)),
Response::DisplayMessage { message: _, id, .. } => {
match id.as_str() {
"inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)),
Response::DisplayMessage { message: _, msg_type } => {
match msg_type {
MsgType::InboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
@ -192,27 +188,27 @@ impl Client {
}
pub async fn delete_inbox_msg(&mut self, message_id: u8) -> BankResult<(), BankError> {
let (message, id, _) = 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?);
let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::ReadInbox { message_id: message_id as i8 }, check_message_id!(MsgType::DeleteInboxSuccess | MsgType::DeleteInboxFailCredentials)).await?);
info!("{}", message);
match id.as_str() {
"read_inbox.success" => Ok(()),
"read_inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)),
match msg_type {
MsgType::DeleteInboxSuccess => Ok(()),
MsgType::DeleteInboxFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
pub async fn get_pm_size(&mut self) -> BankResult<u8, BankError> {
let (_, id, value) = display_message(
let (_, msg_type) = display_message(
self.client
.send_authenticated(
AuthenticatedMessage::GetPmInbox { message_id: -1 },
check_message_id!("pm_message_count" | "pm.fail.credentials"),
check_message_id!(MsgType::PmMessageCount { message_count: _ } | MsgType::PmFailCredentials),
)
.await?);
match id.as_str() {
"pm_message_count" => Ok(value as u8),
"pm.fail.credentials" => Err(report!(BankError::AuthenticationError)),
match msg_type {
MsgType::PmMessageCount { message_count } => Ok(message_count as u8),
MsgType::PmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
@ -221,17 +217,17 @@ impl Client {
let message = self.client.send_authenticated(AuthenticatedMessage::GetPmInbox { message_id: id }, |response| {
match &response.message {
Response::PmInbox { .. } => true,
Response::DisplayMessage { message: _, id, .. } => {
id.as_str().eq("pm.fail.credentials")
Response::DisplayMessage { message: _, msg_type } => {
msg_type.eq(&MsgType::PmFailCredentials)
}
_ => false,
}
}).await?;
match message {
Response::PmInbox { message_id, message } => Ok((message_id, message)),
Response::DisplayMessage { message: _, id, .. } => {
match id.as_str() {
"pm.fail.credentials" => Err(report!(BankError::AuthenticationError)),
Response::DisplayMessage { message: _, msg_type } => {
match msg_type {
MsgType::PmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
@ -239,53 +235,232 @@ impl Client {
}
}
pub async fn delete_pm_msg(&mut self, message_id: u8) -> BankResult<(), BankError> {
let (message, id, _) = 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?);
info!("{}", message);
match id.as_str() {
"read_pm.success" => Ok(()),
"read_pm.fail.credentials" => Err(report!(BankError::AuthenticationError)),
pub async fn delete_pm_msg(&mut self, message_id: u8, silent: bool) -> BankResult<(), BankError> {
let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::ReadPmInbox { message_id: message_id as i8 }, check_message_id!(MsgType::DeletePmSuccess | MsgType::DeletePmFailCredentials)).await?);
if !silent {
info!("{}", message);
}
match msg_type {
MsgType::DeletePmSuccess => Ok(()),
MsgType::DeletePmFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
pub async fn get_balance(&mut self) -> BankResult<u32, BankError> {
let (_, id, value) = display_message(self.client.send_authenticated(
AuthenticatedMessage::GetBalance, check_message_id!("balance.success" | "balance.fail.credentials")).await?);
match id.as_str() {
"balance.success" => Ok(value),
"balance.fail.credentials" => Err(report!(BankError::AuthenticationError)),
let (_, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::GetBalance, check_message_id!(MsgType::BalanceSuccess { balance: _ } | MsgType::BalanceFailCredentials)).await?);
match msg_type {
MsgType::BalanceSuccess { balance } => Ok(balance),
MsgType::BalanceFailCredentials => Err(report!(BankError::AuthenticationError)),
_ => panic!("Unexpected message"),
}
}
pub async fn pay(&mut self, destination: String, amount: u32) -> BankResult<(), BankError> {
let (message, id, _) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Pay { destination, amount }, check_message_id!("pay.success" | "pay.fail.unknown_error" |
"pay.fail.not_enough_money" | "pay.fail.credentials" | "pay.fail.unknown_dest" | "pay.fail.orig_user_unknown" | "pay.fail.negative_amount")).await?);
let (message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::Pay { destination, amount }, check_message_id!(MsgType::PaySuccess | MsgType::PayFailInternalError |
MsgType::PayFailNotEnoughMoney | MsgType::PayFailCredentials | MsgType::PayFailUnknownDestination |
MsgType::PayFailUnknownUsername | MsgType::PayFailNegativeAmount)).await?);
info!("{}", message);
match id.as_str() {
"pay.success" => Ok(()),
"pay.fail.not_enough_money" => Err(report!(BankError::NotEnoughMoney)),
"pay.fail.unknown_dest" => Err(report!(BankError::DestinationUnknown)),
"pay.fail.credentials" => Err(report!(BankError::AuthenticationError)),
"pay.fail.orig_user_unknown" => Err(report!(BankError::AuthenticationError)),
"pay.fail.negative_amount" => Err(report!(BankError::InternalError)),
"pay.fail.unknown_error" => Err(report!(BankError::InternalError)),
match msg_type {
MsgType::PaySuccess => Ok(()),
MsgType::PayFailNotEnoughMoney => Err(report!(BankError::NotEnoughMoney)),
MsgType::PayFailUnknownDestination => Err(report!(BankError::DestinationUnknown)),
MsgType::PayFailCredentials => Err(report!(BankError::AuthenticationError)),
MsgType::PayFailUnknownUsername => Err(report!(BankError::AuthenticationError)),
MsgType::PayFailNegativeAmount => Err(report!(BankError::InternalError)),
MsgType::PayFailInternalError => Err(report!(BankError::InternalError)),
_ => panic!("Unexpected message"),
}
}
pub async fn send_pm(&mut self, destination: String, message: String) -> BankResult<(), BankError> {
let (message, id, _) = 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?);
info!("{}", message);
match id.as_str() {
"pm_inbox.send.success" => Ok(()),
"pm_inbox.dest.unknown" => Err(report!(BankError::DestinationUnknown)),
"pm_inbox.fail.credentials" => Err(report!(BankError::AuthenticationError)),
let (server_message, msg_type) = display_message(self.client.send_authenticated(
AuthenticatedMessage::SendPm { destination: destination.clone(), message: message.clone() }, check_message_id!(MsgType::PmSendSuccess | MsgType::PMFailDestinationUnknown | MsgType::PmFailCredentials)).await?);
info!("{}", server_message);
match msg_type {
MsgType::PmSendSuccess => {
if self.config.general.use_pm_history {
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"),
}
}
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")]
NotEnoughMoney,
#[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>;
@ -191,20 +193,27 @@ pub enum AuthenticatedMessage {
message: String,
},
Motd,
CustomDataInsert {
#[serde(rename = "value3")]
key: String,
#[serde(rename = "value4")]
data: String,
},
CustomDataGet {
#[serde(rename = "value3")]
key: String,
},
}
#[derive(Deserialize, Clone, Debug)]
#[serde(tag = "action", rename_all = "camelCase")]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum Response {
#[serde(rename = "displayMessage")]
DisplayMessage {
#[serde(rename = "value1")]
message: String,
#[serde(rename = "value2")]
id: String,
#[serde(rename = "value3", default = "default_u32")]
value: u32,
#[serde(rename = "value4", default = "String::new")]
value2: String,
#[serde(flatten)]
msg_type: MsgType,
},
Inbox {
#[serde(rename = "value1")]
@ -212,7 +221,6 @@ pub enum Response {
#[serde(rename = "value2")]
message: String,
},
#[serde(rename = "pm_inbox")]
PmInbox {
#[serde(rename = "value1")]
message_id: u8,
@ -223,6 +231,95 @@ pub enum Response {
#[serde(rename = "value1")]
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)]
@ -237,6 +334,7 @@ fn default_i32() -> i32 {
-1
}
#[allow(dead_code)]
fn default_u32() -> u32 {
0
}
@ -252,10 +350,10 @@ impl ezsockets::ClientExt for Handler {
serde_json::from_str(text.as_str()).expect("Error decoding message");
if message.id == self.id || message.id == -1 {
match &message.message {
Response::DisplayMessage { message: _, id, value, .. } => {
match id.as_str() {
"auth.success" => {
self.id = value.clone() as i32;
Response::DisplayMessage { message: _, msg_type } => {
match msg_type {
MsgType::AuthSuccess { new_id } => {
self.id = new_id.clone() as i32;
}
_ => {}
}

View File