I'm a full time student, and hobby developer
๐ฉโ๐ป I'm currently working on silly little apps to make my life easier
๐ง I'm currently learning C, C++ & Rust
๐ She/Her
Discord Rich Presence in Rust
License: MIT License
Functions such as set_activity
and others can run for a long time. In these cases, having it return a future by default would be better.
Describe the bug
When running the following code, set_activity
panics. The activity is visible for a few seconds in Discord.
use discord_presence::{Client, Event};
use std::{env, thread, time};
fn main() {
// Get our main status message
let state_message = env::args().nth(1).expect("Requires at least one argument");
// Create the client
let mut drpc = Client::new(/* ... */);
// Register event handlers with the corresponding methods
drpc.on_ready(|_ctx| {
println!("ready?");
})
.persist();
// Start up the client connection, so that we can actually send and receive stuff
drpc.start();
drpc.block_until_event(Event::Ready).unwrap();
// Set the activity
drpc.set_activity(|act| act.state(state_message))
.expect("Failed to set activity");
// Wait 10 seconds before exiting
thread::sleep(time::Duration::from_secs(5));
}
ready?
thread 'main' panicked at src/main.rs:23:10:
Failed to set activity: JsonError(Error("missing field `buttons`", line: 1, column: 107))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
To Reproduce
Steps to reproduce the behavior:
set_activity
will panic.Expected behavior
The code should run as intended; the activity should be visible for 5 seconds, after which the program terminates.
Desktop (please complete the following information):
Additional context
Adding a button to the activity makes the program hang forever.
drpc.set_activity(|act| {
act.state(state_message)
.append_buttons(|btn| btn.label("Testing"))
}).expect("Failed to set activity");
Ubuntu 22.04's Snap-based Discord uses a different directory for configuration and IPC, resulting in the library not being able to find the IPC socket. The library will infinitely hang.
It looks like an issue for an older version of Discord was previously filed and closed. Perhaps Ubuntu changed how Snaps work.
A debian package-based Discord works fine.
Versions:
Describe the bug
If Discord is not running, or closes while the connection is open, an error is logged:
ERROR discord_presence::connection::manager: Failed to connect: IoError(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })
The connection manager then breaks from the send/receive loop, but the error itself is not propagated to the consumer, making it very difficult (or impossible) to actually detect and handle these situations (unless I'm missing something).
To Reproduce
Steps to reproduce the behavior:
let client = DiscordClient::new("id");
client.on_ready(|_| println!("ready"));
client.on_error(|err| println!("error: {err:?}"));
client.start();
on_error
.Expected behavior
These connection errors should be propagated to the client's on_error
handler, making it possible to capture and handle. It should be possible to capture connection errors when the library first starts, and at any point thereafter.
Desktop (please complete the following information):
๐ Hi @jewlexx & community
What I'm going to propose could very well be "out of scope" for this project and might be possible to abstract the IPC logic into a standalone project which this could consume.
Allow for more generic usage of the IPC that is documented as RPC private beta and probably never going public any time soon ๐ .
There are many things you can do when interacting with the discord client via RPC. I use a bunch of the RPC API in hacksore/overlayed but I want to try out Tauri and currently there is not a rust impl of a discord IPC client.
Keep in mind I'm just the local JS dev so my knowledge of rust is basically non-existent.
use discord_presence::{Client as DiscordRPC, DiscordCommand, DiscordEvent};
fn main() {
let mut drpc = DiscordRPC::new(905987126099836938);
// client is ready so we can login
drpc.on_ready(|_ctx| {
println!("client connected!");
// login with a valid app access token that has "rpc" scope as well as "trusted testers"
drpc.login("42069420");
// assuming login was succesful we can try to sub to moar events
drpc.subscribe(DiscordCommand::GetSelectedVoiceChannel);
drpc.subscribe(DiscordCommand::VoiceChannelSelect);
});
// generic message handler to capture anything from the IPC
drpc.on_message(|ctx| {
println!("Command: {:?}", ctx.command);
println!("Event: {:?}", ctx.event);
println!("Data: {:?}", ctx.data);
// sent when the client joins a voice channel
if (ctx.event == DiscordEvent::VoiceChannelSelect) {
// TODO: do something with this
}
});
drpc.start();
}
Initially, I started trying to hack something together hacksore/discord-ipc-rust but then I stumbled across this repo.
In any case thanks for all the work that went into this project and no hard feelings if my request can't be done.
I need a way to close/exit/quit the thread, I just need the first ready event to get the user's discord id
impl Default for Ravenwood {
fn default() -> Self {
println!("[Ravenwood] Attempting to get your Discord ID using RPC");
let client = Client::new();
let mut discord_id = USER_ID;
let mut discord_rpc = DiscordRPC::new(CLIENT_ID);
discord_rpc.on_ready(|ctx| {
let a = ctx.event;
let b = a.as_object();
});
let handle = discord_rpc.start();
// std::thread::sleep(Duration::from_secs(15));
discord_rpc.block_until_event(Event::Ready).unwrap();
if discord_id == USER_ID {
println!("[Ravenwood] Couldn't get your Discord ID, please make sure Discord is open");
println!("[Ravenwood] Defaulting to the ID of the developer of this program, ShayBox");
}
Self { client, discord_id }
}
}
Letting the variables drop out of scope just causes the thread to crash when it receives an event
Also, A model struct for the ready event would be nice
Maybe this can be done using Abortable
https://docs.rs/futures/latest/futures/future/struct.Abortable.html
Hey, thanks a bunch for this plugin!
I'm noticing that changing my presence seems to hang my game. I have the following system:
fn update_presence(
app_state: Res<State<AppState>>,
mut last_state: Local<Option<AppState>>,
mut activity_state: ResMut<ActivityState>,
level: Query<&Level, Changed<Level>>,
) {
let mut changed = false;
for _ in level.iter() {
println!("New level");
changed = true;
}
if changed || *last_state != Some(*app_state.current()) {
println!(
"App state changed: {:?} != {:?}",
*last_state,
Some(*app_state.current())
);
match app_state.current() {
AppState::Loading | AppState::Paused => {}
AppState::InGame
| AppState::Exploration
| AppState::BetweenLives
| AppState::LevelUp => {
for level in level.iter() {
// activity_state.details = Some(format!("Level {}", **level));
}
}
_ => {
// activity_state.details = None;
}
}
}
*last_state = Some(*app_state.current());
}
I've confirmed that these conditionals run when they should, and not excessively. As shown, this system doesn't seem to hang the game. However, if I uncomment the line that changes the state details, the game hangs. Likewise, if I uncomment more of the matches so I can set state updates more liberally, the game hangs at each point where an update is sent.
I'm guessing the client isn't async? Wondering if it'd make sense to run the updates in a separate task?
Thanks again.
Describe the bug
When I call Client.shutdown() it returns an Err(NotStarted)
I believe it's succeeding but still returning an Err, because if I use block_on instead, it doesn't error.
To Reproduce
lazy_static::lazy_static! {
pub static ref USER: Option<PartialUser> = {
let user_event = Arc::new(RwLock::new(None));
let user_clone = user_event.clone();
let mut client = Client::new(CLIENT_ID);
client.on_ready(move |ctx| {
if let EventData::Ready(event) = ctx.event {
*user_event.write() = event.user;
};
}).persist();
client.start();
// block_until_event will never timeout
std::thread::sleep(Duration::from_secs(5));
client.shutdown().expect("Failed to stop RPC thread");
let user = user_clone.read();
user.clone()
};
}
Expected behavior
Client.shutdown() to return Ok if it successfully shut down the thread.
Desktop:
I'm developing a music app, and I would really like the ability to have the discord status say "Listening to <music application>" or "Listening on <music application>". Just like spotify. Right now it says "Playing <discord developer application name>"
Is there any way to change this? Or is this somehow changed through the discord dev api? I've looked through the docs.rs page, and didn't find much...
Reference in bevy-discord-rpc https://github.com/jewlexx/bevy-discord-rpc/blob/db1a850ac336d0a1c60e6812946805098a6ef2ed/src/lib.rs#L85
Is your feature request related to a problem? Please describe.
Internal rate limiter, to prevent devs from hitting Discord rate limits by default.
Describe the solution you'd like. Also mention of you would be able to get involved in implementing the solution
Implement a rate limiter that follows the Discord rate limit and only runs executions when the rate limit is refreshed.
Is your feature request related to a problem? Please describe.
Right now it doesn't look like it's possible to remove event handlers. This is unfortunate, because it means registering an event handler is always a memory leak.
Describe the solution you'd like. Also mention of you would be able to get involved in implementing the solution
Ideally the on_event
method would return a value that de-registers the event callback when it is dropped. The old behavior could be restored by using std::mem::forget
on the returned value.
Describe alternatives you've considered
Another option would be to have some sort of delete_handler
or remove_event_handler
method on the Client. This has the downside of allowing you to try and deregister a listener multiple times or deregister one that doesn't exist, making some kind of runtime error.
So I tried to run one of the examples, e.g. the discord_presence_subscriber, I also tried the discord_presence
example.
Created a Discord app at Discord Developer Portal, changed the ID with my own and ran the example(s), but I keep getting the error: Failed to set presence: Connection has not been started
.
I must be missing something but I'm not sure what.
Info:
Add better error types for those raised in the error event.
Pretty sure I'm just doing something wrong here, but I can't figure out why set_activity
runs before start
in my program.
use discord_presence::Client;
fn main() {
let mut client = Client::new(1066249445282422784);
client.on_ready(|_| println!("Ready"));
client.on_error(|ctx| eprintln!("{}", ctx.event));
let thread = client.start();
if let Err(err) = client.set_activity(|act| act.state("State Text")) {
eprintln!("{}", err);
}
thread.join().unwrap()
}
This outputs the following:
Connection has not been started
Ready
If someone could please explain to me what I'm doing wrong, I would greatly appreciate it!
Describe the bug
It prints the following instead of showing presence:
Failed to set presence: No message sent
Code
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use discord_presence::Client as DiscordRPC;
use std::env;
fn main() {
let mut drpc = DiscordRPC::new(1002568032041832578);
drpc.on_ready(|_ctx| {
println!("READY!");
});
drpc.on_error(|ctx| {
eprintln!("An error occured, {}", ctx.event);
});
drpc.start();
if let Err(why) = drpc.set_activity(|a| {
a.state("Running examples").assets(|ass| {
ass.large_image("1024x1024")
.large_text("Building a Discord Bot")
.small_image("512x512")
.small_text("With Hydrazine")
})
}) {
println!("Failed to set presence: {}", why);
}
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("Unknown Error Occurred While Running Application!");
}
im writing plugin for lapce . can i use rpc with wasm32-wasi ?
The Discord GameSDK theoretically provides an easier way to interface with Discord RPC, without interacting with system pipes manually.
I need to specify an Option<PartialUser>
response, but PartialUser
isn't public.
lazy_static::lazy_static! {
pub static ref USER: Option<PartialUser> = {
let user = Arc::new(RwLock::new(None));
let user_clone = user.clone();
let mut client = Client::new(CLIENT_ID);
client.on_ready(move |ctx| {
let EventData::Ready(event) = ctx.event else {
return;
};
*user.write() = event.user;
});
client.start();
std::thread::sleep(Duration::from_secs(5));
client.shutdown().expect("Failed to stop RPC thread");
let user = user_clone.read();
user.clone()
};
}
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
This repository currently has no open or pending branches.
Cargo.toml
byteorder 1.5
bytes 1.6
cfg-if 1.0
crossbeam-channel 0.5
num-derive 0.4
num-traits 0.2
parking_lot 0.12
paste 1.0
quork 0.7
serde_json 1.0
thiserror 1.0
tracing 0.1
serde 1.0
uuid 1.8
anyhow 1.0
ctrlc 3.4
rusty-hook 0.11
tracing-subscriber 0.3
version-sync 0.9
named_pipe 0.4
.github/workflows/publish.yml
actions/checkout v4
.github/workflows/rust.yml
actions/checkout v4
Swatinem/rust-cache v2
Is your feature request related to a problem? Please describe.
There is no consise and easy way to get the ready status of the RPC without using an event listener.
Describe the solution you'd like. Also mention of you would be able to get involved in implementing the solution
the READY
AtomicBool to be un-privated.
Describe alternatives you've considered
Using a event listener, but I couldn't move refs around properly and it didn't seem to work that well. This seems like a built-in thing which would be easier to use.
The rpc application is normally created on https://discord.com/developers/applications/
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(RPCPlugin(RPCConfig{
app_id: token(),
show_time: true,
}))
.add_system(update_presence)
.run();
}
fn update_presence(
mut state: ResMut<ActivityState>,
){
state.instance = Some(true);
state.details = Some("Hello World".to_string());
state.state = Some("This is state".to_string());
}
Function token()
simply returns my token variable
But I get this error for some reason:
Failed to connect: IoError(Os { code: 2, kind: NotFound, message: "No such file or directory" })
EDIT:
OS: Ubuntu Budgie 20.04
Kernel: 5.13.0
Library Version: 0.2.2 (0.5.2)
Bevy Version: 0.7
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.