Files
AstralRinth/packages/app-lib/src/state/discord.rs
Josiah Glosson b23d3e674f Update Rust & Java dependencies (#4540)
* Update Java dependencies

* Baselint lint fixes

* Update Rust version

* Update actix-files 0.6.6 -> 0.6.8

* Update actix-http 3.11.0 -> 3.11.2

* Update actix-rt 2.10.0 -> 2.11.0

* Update async_zip 0.0.17 -> 0.0.18

* Update async-compression 0.4.27 -> 0.4.32

* Update async-trait 0.1.88 -> 0.1.89

* Update async-tungstenite 0.30.0 -> 0.31.0

* Update const_format 0.2.34 -> 0.2.35

* Update bitflags 2.9.1 -> 2.9.4

* Update bytemuck 1.23.1 -> 1.24.0

* Update typed-path 0.11.0 -> 0.12.0

* Update chrono 0.4.41 -> 0.4.42

* Update cidre 0.11.2 -> 0.11.3

* Update clap 4.5.43 -> 4.5.48

* Update data-url 0.3.1 -> 0.3.2

* Update discord-rich-presence 0.2.5 -> 1.0.0

* Update enumset 1.1.7 -> 1.1.10

* Update flate2 1.1.2 -> 1.1.4

* Update hyper 1.6.0 -> 1.7.0

* Update hyper-util 0.1.16 -> 0.1.17

* Update iana-time-zone 0.1.63 -> 0.1.64

* Update image 0.25.6 -> 0.25.8

* Update indexmap 2.10.0 -> 2.11.4

* Update json-patch 4.0.0 -> 4.1.0

* Update meilisearch-sdk 0.29.1 -> 0.30.0

* Update clickhouse 0.13.3 -> 0.14.0

* Fix some prettier things

* Update lettre 0.11.18 -> 0.11.19

* Update phf 0.12.1 -> 0.13.1

* Update png 0.17.16 -> 0.18.0

* Update quick-xml 0.38.1 -> 0.38.3

* Update redis 0.32.4 -> 0.32.7

* Update regex 1.11.1 -> 1.11.3

* Update reqwest 0.12.22 -> 0.12.23

* Update rust_decimal 1.37.2 -> 1.38.0

* Update rust-s3 0.35.1 -> 0.37.0

* Update serde 1.0.219 -> 1.0.228

* Update serde_bytes 0.11.17 -> 0.11.19

* Update serde_json 1.0.142 -> 1.0.145

* Update serde_with 3.14.0 -> 3.15.0

* Update sentry 0.42.0 -> 0.45.0 and sentry-actix 0.42.0 -> 0.45.0

* Update spdx 0.10.9 -> 0.12.0

* Update sysinfo 0.36.1 -> 0.37.2

* Update tauri 2.7.0 -> 2.8.5

* Update tauri-build 2.3.1 -> 2.4.1

* Update tauri-plugin-deep-link 2.4.1 -> 2.4.3

* Update tauri-plugin-dialog 2.3.2 -> 2.4.0

* Update tauri-plugin-http 2.5.1 -> 2.5.2

* Update tauri-plugin-opener 2.4.0 -> 2.5.0

* Update tauri-plugin-os 2.3.0 -> 2.3.1

* Update tauri-plugin-single-instance 2.3.2 -> 2.3.4

* Update tempfile 3.20.0 -> 3.23.0

* Update thiserror 2.0.12 -> 2.0.17

* Update tracing-subscriber 0.3.19 -> 0.3.20

* Update url 2.5.4 -> 2.5.7

* Update uuid 1.17.0 -> 1.18.1

* Update webp 0.3.0 -> 0.3.1

* Update whoami 1.6.0 -> 1.6.1

* Note that windows and windows-core can't be updated yet

* Update zbus 5.9.0 -> 5.11.0

* Update zip 4.3.0 -> 6.0.0

* Fix build

* Enforce rustls crypto provider

* Refresh Cargo.lock

* Update transitive dependencies

* Bump Gradle usage to Java 17

* Use ubuntu-latest consistently across workflows

* Fix lint

* Fix lint in Rust

* Update native-dialog 0.9.0 -> 0.9.2

* Update regex 1.11.3 -> 1.12.2

* Update reqwest 0.12.23 -> 0.12.24

* Update rust_decimal 1.38.0 -> 1.39.0

* Remaining lock-only updates

* chore: move TLS impl of some other dependencies to aws-lc-rs

The AWS bloatware "virus" expands by sheer force of widespread adoption
by the ecosystem... 🫣

* chore(fmt): run Tombi

---------

Co-authored-by: Alejandro González <me@alegon.dev>
2025-10-15 20:45:47 +00:00

154 lines
5.5 KiB
Rust

use std::sync::{Arc, atomic::AtomicBool};
use discord_rich_presence::{
DiscordIpc, DiscordIpcClient,
activity::{Activity, Assets},
};
use tokio::sync::RwLock;
use crate::State;
use crate::state::Profile;
pub struct DiscordGuard {
client: Arc<RwLock<DiscordIpcClient>>,
connected: Arc<AtomicBool>,
}
impl DiscordGuard {
/// Initialize discord IPC client, and attempt to connect to it
/// If it fails, it will still return a DiscordGuard, but the client will be unconnected
pub fn init() -> crate::Result<DiscordGuard> {
let dipc = DiscordIpcClient::new("1123683254248148992");
Ok(DiscordGuard {
client: Arc::new(RwLock::new(dipc)),
connected: Arc::new(AtomicBool::new(false)),
})
}
/// If the client failed connecting during init(), this will check for connection and attempt to reconnect
/// This MUST be called first in any client method that requires a connection, because those can PANIC if the client is not connected
/// (No connection is different than a failed connection, the latter will not panic and can be retried)
pub async fn retry_if_not_ready(&self) -> bool {
let mut client = self.client.write().await;
if !self.connected.load(std::sync::atomic::Ordering::Relaxed) {
if client.connect().is_ok() {
self.connected
.store(true, std::sync::atomic::Ordering::Relaxed);
return true;
}
return false;
}
true
}
/// Set the activity to the given message
/// First checks if discord is disabled, and if so, clear the activity instead
pub async fn set_activity(
&self,
msg: &str,
reconnect_if_fail: bool,
) -> crate::Result<()> {
// Check if discord is disabled, and if so, clear the activity instead
let state = State::get().await?;
let settings = crate::state::Settings::get(&state.pool).await?;
if !settings.discord_rpc {
Ok(self.clear_activity(true).await?)
} else {
Ok(self.force_set_activity(msg, reconnect_if_fail).await?)
}
}
/// Sets the activity to the given message, regardless of if discord is disabled or offline
/// Should not be used except for in the above method, or if it is already known that discord is enabled (specifically for state initialization) and we are connected to the internet
pub async fn force_set_activity(
&self,
msg: &str,
reconnect_if_fail: bool,
) -> crate::Result<()> {
// Attempt to connect if not connected. Do not continue if it fails, as the client.set_activity can panic if it never was connected
if !self.retry_if_not_ready().await {
return Ok(());
}
let activity = Activity::new().state(msg).assets(
Assets::new()
.large_image("modrinth_simple")
.large_text("Modrinth Logo"),
);
// Attempt to set the activity
// If the existing connection fails, attempt to reconnect and try again
let mut client: tokio::sync::RwLockWriteGuard<'_, DiscordIpcClient> =
self.client.write().await;
let res = client.set_activity(activity.clone());
if reconnect_if_fail {
if let Err(_e) = res {
client.reconnect()?;
return Ok(client.set_activity(activity)?); // try again, but don't reconnect if it fails again
}
} else {
res?;
}
Ok(())
}
/// Clear the activity entirely ('disabling' the RPC until the next set_activity)
pub async fn clear_activity(
&self,
reconnect_if_fail: bool,
) -> crate::Result<()> {
// Attempt to connect if not connected. Do not continue if it fails, as the client.clear_activity can panic if it never was connected
if !self.retry_if_not_ready().await {
return Ok(());
}
// Attempt to clear the activity
// If the existing connection fails, attempt to reconnect and try again
let mut client = self.client.write().await;
let res = client.clear_activity();
if reconnect_if_fail {
if res.is_err() {
client.reconnect()?;
return Ok(client.clear_activity()?); // try again, but don't reconnect if it fails again
}
} else {
res?;
}
Ok(())
}
/// Clear the activity, but if there is a running profile, set the activity to that instead
pub async fn clear_to_default(
&self,
reconnect_if_fail: bool,
) -> crate::Result<()> {
let state = State::get().await?;
let settings = crate::state::Settings::get(&state.pool).await?;
if !settings.discord_rpc {
println!("Discord is disabled, clearing activity");
return self.clear_activity(true).await;
}
let running_profiles = state.process_manager.get_all();
if let Some(existing_child) = running_profiles.first() {
let prof =
Profile::get(&existing_child.profile_path, &state.pool).await?;
if let Some(prof) = prof {
self.set_activity(
&format!("Playing {}", prof.name),
reconnect_if_fail,
)
.await?;
}
} else {
self.set_activity("Idling...", reconnect_if_fail).await?;
}
Ok(())
}
}