discord_sdk/
overlay.rs

1//! Provides types and functionality for the Discord [Overlay](https://discord.com/developers/docs/game-sdk/overlay)
2
3pub mod events;
4
5use crate::{Command, CommandKind, Error};
6use serde::Serialize;
7
8#[derive(Serialize)]
9struct OverlayToggle {
10    /// Our process id, this lets Discord know what process it should try
11    /// to show the overlay in
12    pid: u32,
13    #[serde(rename = "locked")]
14    visibility: Visibility,
15}
16
17impl OverlayToggle {
18    fn new(visibility: Visibility) -> Self {
19        Self {
20            pid: std::process::id(),
21            visibility,
22        }
23    }
24}
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub enum Visibility {
28    Visible,
29    Hidden,
30}
31
32impl Serialize for Visibility {
33    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34    where
35        S: serde::Serializer,
36    {
37        serializer.serialize_bool(!(*self == Self::Visible))
38    }
39}
40
41impl<'de> serde::Deserialize<'de> for Visibility {
42    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43    where
44        D: serde::Deserializer<'de>,
45    {
46        use serde::de;
47
48        struct Visitor;
49
50        impl de::Visitor<'_> for Visitor {
51            type Value = Visibility;
52
53            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54                formatter.write_str("a boolean")
55            }
56
57            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
58            where
59                E: de::Error,
60            {
61                Ok(if value {
62                    Visibility::Hidden
63                } else {
64                    Visibility::Visible
65                })
66            }
67        }
68
69        deserializer.deserialize_bool(Visitor)
70    }
71}
72
73#[derive(Copy, Clone, PartialEq, Eq, serde_repr::Serialize_repr)]
74#[repr(u8)]
75pub enum InviteAction {
76    Join = 1,
77    Spectate = 2,
78}
79
80#[derive(Serialize)]
81pub(crate) struct OverlayPidArgs {
82    pid: u32,
83}
84
85impl OverlayPidArgs {
86    pub(crate) fn new() -> Self {
87        Self {
88            pid: std::process::id(),
89        }
90    }
91}
92
93impl crate::Discord {
94    /// Opens or closes the overlay. If the overlay is not enabled this will
95    /// instead focus the Discord app itself.
96    ///
97    /// [API docs](https://discord.com/developers/docs/game-sdk/overlay#setlocked)
98    pub async fn set_overlay_visibility(&self, visibility: Visibility) -> Result<(), Error> {
99        let rx = self.send_rpc(
100            CommandKind::SetOverlayVisibility,
101            OverlayToggle::new(visibility),
102        )?;
103
104        handle_response!(rx, Command::SetOverlayVisibility => {
105            Ok(())
106        })
107    }
108
109    /// Opens the overlay modal for sending game invitations to users, channels,
110    /// and servers.
111    ///
112    /// # Errors
113    /// If you do not have a valid activity with all the required fields, this
114    /// call will error. See
115    /// [Activity Action Field Requirements](https://discord.com/developers/docs/game-sdk/activities#activity-action-field-requirements)
116    /// for the fields required to have join and spectate invites function properly.
117    ///
118    /// [API docs](https://discord.com/developers/docs/game-sdk/overlay#openactivityinvite)
119    pub async fn open_activity_invite(&self, action: InviteAction) -> Result<(), Error> {
120        #[derive(Serialize)]
121        struct OpenInviteModal {
122            /// Our process id, this lets Discord know what process it should try
123            /// to show the overlay in
124            pid: u32,
125            #[serde(rename = "type")]
126            kind: InviteAction,
127        }
128
129        let rx = self.send_rpc(
130            CommandKind::OpenOverlayActivityInvite,
131            OpenInviteModal {
132                pid: std::process::id(),
133                kind: action,
134            },
135        )?;
136
137        handle_response!(rx, Command::OpenOverlayActivityInvite => {
138            Ok(())
139        })
140    }
141
142    /// Opens the overlay modal for joining a Discord guild, given its invite code.
143    /// Unlike the normal SDK, this method automatically parses the code from
144    /// the provided string so you don't need to do it yourself.
145    ///
146    /// Note that just because the result might be [`Result::Ok`] doesn't
147    /// necessarily mean the user accepted the invite.
148    ///
149    /// [API docs](https://discord.com/developers/docs/game-sdk/overlay#openguildinvite)
150    pub async fn open_guild_invite(&self, code: impl AsRef<str>) -> Result<(), Error> {
151        let mut code = code.as_ref();
152
153        if let Some(rest) = code.strip_prefix("https://") {
154            code = rest;
155        }
156
157        if let Some(rest) = code.strip_prefix("discord.gg/") {
158            code = rest;
159        } else if let Some(rest) = code.strip_prefix("discordapp.com/invite/") {
160            code = rest;
161        }
162
163        #[derive(Serialize)]
164        struct OpenGuildInviteModal<'stack> {
165            pid: u32,
166            code: &'stack str,
167        }
168
169        let rx = self.send_rpc(
170            CommandKind::OpenOverlayGuildInvite,
171            OpenGuildInviteModal {
172                pid: std::process::id(),
173                code,
174            },
175        )?;
176
177        handle_response!(rx, Command::OpenOverlayGuildInvite => {
178            Ok(())
179        })
180    }
181
182    /// Opens the overlay widget for voice settings for the currently connected application.
183    ///
184    /// [API docs](https://discord.com/developers/docs/game-sdk/overlay#openvoicesettings)
185    pub async fn open_voice_settings(&self) -> Result<(), Error> {
186        let rx = self.send_rpc(CommandKind::OpenOverlayVoiceSettings, OverlayPidArgs::new())?;
187
188        handle_response!(rx, Command::OpenOverlayVoiceSettings => {
189            Ok(())
190        })
191    }
192}