Code Monkey home page Code Monkey logo

discord-rich-presence's People

Contributors

bond-009 avatar kpids avatar lilithium-hydride avatar paulemeister avatar sardonicism-04 avatar tofixrs avatar trickybestia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

discord-rich-presence's Issues

Changes to `Activity` design

Some recent issues have brought up some concerns with the current structure of the Activity struct and the way it works. I'm currently working on re-designing these interfaces to address these issues and hopefully be overall easier to use. These changes are heavily breaking and would/will accompany a future major version update.

Changes

Note
Activity and ActivityBuilder refer to all models provided by the library (i.e. Timestamps and TimestampsBuilder)

My current idea is to separate the current usage flow into distinct Activity and ActivityBuilder structs, implementing a consuming builder design pattern. My working design for this would look something like this in practice:

// A default `ActivityBuilder` supports chaining similar to that of the current `Activity` struct
let mut activity = activity::ActivityBuilder::default()
    .state("State")
    .details("Details")
    .assets(
        activity::AssetsBuilder::default()
            .large_image("large-image")
            .large_text("Large text")
            .build(),
    )
    .buttons(vec![activity::Button::new(
        "A button",
        "https://example.com",
    )])
    .timestamps(activity::TimestampsBuilder::default().start(1).build())
    .build();

// Individual fields on a built model can now be updated like so
activity.set_party(
    activity::PartyBuilder::default()
        .id("some-id")
        .size([1, 3])
        .build(),
);

This showcases several things:

  • The ActivityBuilder structs support chaining similar to that of the current design
    • The setters are consumptive, so all calls to them leading up to .build() must be chained (or a new reference has to be created for each setter called)
  • The Activity structs now have set_* methods which take an &mut self. This makes it much easier to update individual model values after the model has been built (#19)

Unrelated to the updates to chaining and construction, none of the library's models require lifetimes anymore, and accept anything that satisfies impl ToString where an &str was previously required - which was the reason the lifetimes were previously in place (also #19).

Discussion

I'm raising this issue to get feedback about the design as I've presented it, and if there are improvements to be had.

My main concern is the added boilerplate. I don't think it's a big issue for the large ActivityBuilder, since that's already a pretty long chain in practice, but as seen in the example code above, it definitely uglifies lines such as

    .timestamps(activity::TimestampsBuilder::default().start(1).build())

Is this beyond the acceptable amount of boilerplate, are there alternative solutions? One idea I scrapped was adding simple ::new functions for models like Timestamps instead of implementing a builder - the issue here is that this would then require arguments to be passed in an Option-friendly form, which still adds boilerplate.


This was a lot of words, so I thank any who read them all, and I'd appreciate feedback/discussion :)

[Help] Clearing Activity

Are you able to clear the activity without closing the connection?
if not, how can i implement this feature in my program

Rich presence automatically disappears after a minute

So I ran into a problem with discord-rich-presence on a Windows machine, it connects successfully and displays the rich presence as it should but after a minute, it eventually disappears.

use std::sync::Mutex;

use crate::commands::configuration::schema::Configuration;
use crate::schema::user_location::UserLocationContent;
use crate::utils::replacer::replace_magic_string;
use discord_presence::{Client, Event};
use lazy_static::lazy_static;

pub static DISCORD_CLIENT_ID: u64 = 1202945591739420702;

lazy_static! {
    pub static ref DISCORD_CLIENT: Mutex<DiscordIpcClient> = Mutex::new(DiscordIpcClient::new());
}

pub struct DiscordIpcClient {
    pub client: Client,
    pub configuration: Option<Configuration>,
    pub data: Option<UserLocationContent>,
}

impl DiscordIpcClient {
    pub fn new() -> Self {
        let mut drpc_client = Client::new(DISCORD_CLIENT_ID);

        drpc_client.start();
        match drpc_client.block_until_event(Event::Ready) {
            Ok(_) => println!("Discord RPC client ready"),
            Err(e) => println!("Error starting discord RPC client: {}", e),
        }

        Self {
            client: drpc_client,
            configuration: Some(Configuration {
                title: "VRCHAT".to_string(),
                description: "{{user.display_name}} is playing rest & sleep".to_string(),
                show_timestamp: true,
                show_player_status: true,
                small_image_key: "This is a small image key".to_string(),
                large_image_key: "This is a large image key".to_string(),
            }),
            data: None,
        }
    }

    pub fn set_activity(&mut self) {
        let config = self.configuration.clone().unwrap();
        let data = self.data.clone().unwrap();

        match self.client.set_activity(|a| {
            a.state(replace_magic_string(config.title, &data))
                .details(replace_magic_string(config.description, &data))
                .assets(|assets| {
                    assets
                        .large_image(data.world.unwrap().image_url.unwrap_or("vrchat".to_string()))
                        .large_text("VRChat")
                        .small_image("vrchat")
                        .small_text("VRChat")
                })
        }) {
            Ok(_) => println!("Discord presence updated"),
            Err(e) => println!("Error updating discord presence: {}", e),
        }
    }

    pub fn set_configuration(&mut self, configuration: Configuration) {
        self.configuration = Some(configuration);
    }

    pub fn set_data(&mut self, data: UserLocationContent) {
        self.data = Some(data);
    }
}

No rich presence

Hey, I'm working on a Tauri app and am trying to connect to Discord RP. I'm literally brand new to rust but here's how I did it:

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            let id = app.listen_global("discord", |event| {
                discord();
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

fn discord() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = DiscordIpcClient::new("my_app_id")?;

    client.connect()?;
    client.set_activity(activity::Activity::new()
        .state("foo")
        .details("bar")
    )?;
    client.close()?;

    println!("test");

    Ok(())
}

When I call the discord() function, "test" prints but my rich presence doesn't appear on Discord. Any idea what I'm doing wrong?

Dynamic LargeImage

Apparently, the large_image asset can be an URL, as it can be observed in PreMiD, however, I have not been able to replicate it with this library, any idea why?

I have been testing it in my branch here, and even though it prints a supposedly valid image (square) in my logging, Discord just ignores it... Any idea why?

image

image link

What is this error?

error: expected one of `!` or `::`, found `<eof>`
 --> tempCodeRunnerFile.rs:1:1
  |
1 | DiscordIpc
  | ^^^^^^^^^^ expected one of `!` or `::`

error: aborting due to 1 previous error

Someone can help me with that?

The code:

use discord_rich_presence::{activity, DiscordIpc, DiscordIpcClient};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = DiscordIpcClient::new("1214134857999982622")?;

    client.connect()?;
    client.set_activity(activity::Activity::new()
        .state("foo")
        .details("bar")
    )?;
    client.close()?;

    Ok(())
}

image
image

I've implemented some features that might be helpful

I did a bit of tinkering on this crate for my project. The implementation is janky, but I've added some features that make it a lot more convenient.

Maybe you can take these ideas and implement them in a better way.

https://github.com/KPidS/tonbun/tree/main/src-tauri/crates/discord-rich-presence

  1. clear_activity method on DiscordIpc trait. Basically I implemented solution from #13 into the crate.

https://github.com/KPidS/tonbun/blob/main/src-tauri/crates/discord-rich-presence/src/discord_ipc.rs#L212

  1. #14 connected field on the DiscordIpcClient now actually works. Not sure if it's the right way to implement it though.

  2. set_activity_safe and clear_activity_safe.

https://github.com/KPidS/tonbun/blob/main/src-tauri/crates/discord-rich-presence/src/discord_ipc.rs#L212

Returns Ok If connectedis set to false, meaning the client isn't supposed to be connected.

If it's true, then tries to set the activity. If set_activity returns Err it will try to reconnect the client, and returns Err only if it failed to reconnect or failed to set_activity after reconnecting.

  1. DiscordIpcClientMutex struct. Wraps around DiscordIpcClient and has enable and disable functions.

https://github.com/KPidS/tonbun/blob/main/src-tauri/crates/discord-rich-presence/src/discord_ipc_mutex.rs

enable will start an interval that is constantly going to try to connect to the client, so you don't have to worry about manually connecting it ever.

This is definitely WIP at the moment, since I need to implement a proper way to stop the interval thread when you disable it.

Right now if you spam enable disable you can launch multiple threads at the same time.

Button click does not seem to work

I'm trying to use this library for a custom presence app and although I can get buttons to show up in the presence, clicking on them seems to do nothing. No browser window opens or anything like that.

I have only tested this on macos with Discord client version 0.0.268.

Just for some concrete examples, I've tried several different urls, such as Button::new("google", "https://google.com") and Button::new("steam", "https://steampowered.com").

Can you confirm that button clicks are currently working as intended? Any ideas on what the problem might be?

Unhandleable SIGPIPE when sending activity to closed client

If I connect() to a running Discord client, close that client, and then send new data to it with set_activity(), the program crashes with Signal: SIGPIPE (Broken pipe). I'd expect that the error gets caught and returned in set_activity()'s result, but this does not happen.

Stack trace:

std::sys::unix::fd::FileDesc::write fd.rs:151
std::sys::unix::pipe::AnonPipe::write pipe.rs:62
std::process::{impl#6}::write process.rs:290
std::process::{impl#5}::write process.rs:271
std::io::Write::write_all<std::os::unix::net::stream::UnixStream> mod.rs:1532
discord_rich_presence::ipc_unix::{impl#1}::write ipc_unix.rs:80
discord_rich_presence::discord_ipc::DiscordIpc::send<discord_rich_presence::ipc_unix::DiscordIpcClient> discord_ipc.rs:114
discord_rich_presence::discord_ipc::DiscordIpc::set_activity<discord_rich_presence::ipc_unix::DiscordIpcClient> discord_ipc.rs:176
rich_presence_mpris::main::{closure#0} main.rs:123

rich_presence_mpris::main::{closure#0} main.rs:123:

match rpc_client.set_activity(payload) {
      Ok(_) => {}
      Err(err) => {
	      eprintln!("Couldn't update Discord status. Is it running?")
      }
}

Useless lifetimes

Hellow, im tring to have some sort of dynamic Activity,
But the fact that activity::Activity uses Option<&'a str> instead of Option<String> + that it's returning self for each function makes that it's rly hard to use imo.
I made a config-like structure to be used in .ron files so i can add or remove parts easily
(I know it's supposed to be chained but i can't find a way to make it work with the optional config that i am using)
Example:

let mut activity = activity::Activity::new();
match activity_config.state {
    Some(state) => {
        debug!("Activity::State set to: {}", state);
        activity = activity.state(state);
    }
    None => debug!("Activity::State not found"),
}
match activity_config.details {
    Some(details) => {
        debug!("Activity::Details set to: {}", details);
        activity = activity.details(details);
    }
    None => debug!("Activity::Details not found"),
}

This is impossible with the current type of activity::Activity
The example above is working if you replace every Option<&'a str> by Option<String> and remove lifetimes on activity::Activity

It might just be me as im not very experienced with lifetimes, but i see no advantage of using Option<&'a str> instead of Option<String>.

Is it possible to check if the client is connected?

Trying to implement automatic reconnection.

First I check if user enabled rich presence, and if it's disabled, I want to check if the client is open, and then close it. Then if it is enabled, I want to start the client, otherwise continue the loop.

I noticed that there is the connected field on the DiscordIpcClient struct, but it was private. Then I tried to make it public, but it just seams that the functionality is just not implemented yet.

Is there a way to check if the client is connected, or maybe a better way to do what I'm trying to do?

#[tauri::command(async)]
async fn discord_start_interval(client: State<'_, DiscordRP>, prefs: State<'_, PrefsStore>) -> Result<(), ()> {

  let mut interval = interval(Duration::from_secs(1));

  loop {

    interval.tick().await;

    let prefs = prefs.0.lock().unwrap();
    let mut client = client.0.lock().unwrap();

    if !prefs.discord_rich_presence_enabled {
      if !client.connected { if client.close().is_err() { continue } };
      break;
    }

    if client.connected { continue };

    match client.connect() {
      Ok(_) => println!("Opened Discord IPC client"),
      Err(_) => println!("Failed to open Discord IPC client"),
    }

    if client.set_activity(Activity::new()
      .state("foo")
      .details("bar") 
    ).is_err() {
      println!("Failed to set activity")
    }

  }

  Ok(())

}

Generic IPC use case

I opened an issue here to talk about more generic usage of the discord IPC/RPC API.

Figured I'd also raise an issues here since my variant Hacksore/discord-ipc-rust uses much of the awesome work you've done.

I'm very new to rust but I am trying to hack something together to support a very basic use case but would love if anyone else is interested on a generic implementation to collaborate.

If you have questions or comments please do let me know ๐Ÿ˜Ž.

Add support for Snap and Flatpak installs

Given this is currently in the middle of a rewrite, and my lack of Rust skills, I thought I'd create an issue instead of a PR. Fairly simply, to access the IPC on Flatpak installations (I imagine this also works for Snap), you have to append the Flatpak path to the path you get from ENV_KEYS.

For example, to access an IPC normally, you would use /run/user/1000/discord-ipc-0. To access it on Flatpak, you would use /run/user/1000/app/com.discordapp.Discord/discord-ipc-0.

The paths should be:

  • Snap - snap.discord
  • Flatpak - app/com.discordapp.Discord

Couldn't connect to the Discord IPC socket on Ubuntu 22.04

This appears to be the same issue as #2

I'm running this on vanilla Ubuntu 22.04 and am receiving "Couldn't connect to the Discord IPC socket".

Desktop Discord client version:

Stable 152532 (a118a24) Host 0.0.20 Linux 64-Bit (5.15.0-48-Generic)

rustc 1.64.0

discord-rich-presence = "0.2.3"

Linux 5.15.0-48-generic #54-Ubuntu SMP Fri Aug 26 13:26:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

I'll be happy to provide more details.

Error when compiling to WASI

I am trying to make a plugin for the Lapce editor, it compiles plugins to WASI (WebAssembly System Interface). Because the editor is relatively new, there is not much information about plugin development.

The problem is, when I use the package, and compile it to WASI, returns 2 errors:

error[E0432]: unresolved import `ipc`                                                                                                                                   
  --> C:\Users\[USERNAME]\.cargo\registry\src\github.com-1ecc6299db9ec823\discord-rich-presence-0.2.3\src\lib.rs:37:9
   |
37 | pub use ipc::DiscordIpcClient;
   |         ^^^ use of undeclared crate or module `ipc`

error[E0433]: failed to resolve: use of undeclared crate or module `ipc`
  --> C:\Users\[USERNAME]\.cargo\registry\src\github.com-1ecc6299db9ec823\discord-rich-presence-0.2.3\src\lib.rs:48:5
   |
48 |     ipc::DiscordIpcClient::new(client_id)
   |     ^^^ use of undeclared crate or module `ipc`

This is the code I am compiling:

//When the user opens Lapce execute this
fn initialize(params: InitializeParams) -> Result<()> {
    let mut client = DiscordIpcClient::new("1000000000000000000")?;

    client.connect()?;
    client.set_activity(activity::Activity::new()
        .state("Testing")
        .details("RPC Rust")
    )?;
    client.close()?;

    Ok(())
}

Or you can also look at the repository.

image
image

Implementation of ActivityType Enum

The ActivityType enum is present in the SDK Docs and, even if it says "ActivityType [...] will be discarded and will not change anything in the client." I can find cases online where such has been said to be possible (here).

Has this been changed recently? Would it be possible to have this implemented?

image

Activity Buttons break Rich Presence

Maybe this is now unsupported by Discord's API, but sending buttons in the Activity struct somehow makes Discord ignore what is sent.

Additionally, the set_activity function does not return any error, which is why I assume that it is simply being ignored.

The Provided example doesn't work

The example in the readme does not work, even tried using different Application IDs but still get the error

Error: "Couldn't connect to the Discord IPC socket"

How to add a second button

I'm still kinda new to rust so bear with me
I can't figure out how to add the second button can you make an example showing how to do it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.