discord_toggle_rpc/
lib.rs1extern crate dotenv;
2mod utils;
3
4use dotenv::dotenv;
5use serde_json::from_str;
6use std::{collections::HashMap, env, error::Error, io};
7use utils::handle_status_error;
8
9const SETTING_ENDPOINTS: &str = "https://discord.com/api/v9/users/@me/settings";
10const CONNECTION_ENDPOINTS: &str = "https://discord.com/api/v9/users/@me/connections";
11
12pub async fn run(mut args: env::Args) {
13 dotenv().ok();
14 let discord_token = env::var("DISCORD_TOKEN").expect("Discord Token is not found");
15
16 args.next();
18
19 match args.next() {
20 Some(arg) => {
21 match_arg(arg, discord_token).await;
22 }
23 None => {
24 eprintln!("Please include arguments.");
25 }
26 };
27}
28
29pub async fn match_arg(arg: String, token: String) {
30 if arg == "game" {
31 match toggle_game_rpc(token).await {
32 Ok(status) => {
33 println!("Show playing game: {}", status);
34 }
35 Err(err) => {
36 eprintln!("An error occured: {}", err);
37 }
38 }
39 } else if arg == "spotify" {
40 match toggle_spotify_rpc(token).await {
41 Ok(status) => {
42 println!("Show listening Spotify: {}", status);
43 }
44 Err(err) => {
45 eprintln!("An error occured: {}", err);
46 }
47 }
48 }
49}
50
51pub async fn toggle_spotify_rpc(token: String) -> Result<serde_json::Value, Box<dyn Error>> {
52 let client = reqwest::Client::new();
53
54 let res = client
55 .get(CONNECTION_ENDPOINTS)
56 .header("authorization", &token)
57 .send()
58 .await?;
59
60 handle_status_error(res.status().as_u16())?;
61
62 let connections: serde_json::Value = from_str(&res.text().await?)?;
63
64 let mut spotify_id: Option<&str> = None;
65 let mut show_activity_spotify: Option<bool> = None;
66
67 for connection in connections.as_array().unwrap().iter() {
68 if connection["type"].as_str().unwrap() == "spotify" {
69 spotify_id = connection["id"].as_str();
70 show_activity_spotify = connection["show_activity"].as_bool();
71 break;
72 }
73 }
74
75 if spotify_id.is_none() {
77 let connection_error =
78 io::Error::new(io::ErrorKind::Other, "Account is not connected to Spotify");
79
80 return Err(Box::new(connection_error));
81 };
82
83 let spotify_id = spotify_id.unwrap();
84 let show_activity_spotify = show_activity_spotify.unwrap();
85
86 let mut body = HashMap::new();
87
88 body.insert("show_activity", !show_activity_spotify);
89
90 let res = client
91 .patch(format!("{}/spotify/{}", CONNECTION_ENDPOINTS, spotify_id))
92 .header("authorization", &token)
93 .json(&body)
94 .send()
95 .await?;
96
97 if let Err(err) = handle_status_error(res.status().as_u16()) {
98 Err(err)
99 } else {
100 let current_settings: serde_json::Value = serde_json::from_str(&res.text().await?)?;
101
102 Ok(current_settings["show_activity"].clone())
103 }
104}
105
106pub async fn toggle_game_rpc(token: String) -> Result<serde_json::Value, Box<dyn Error>> {
107 let client = reqwest::Client::new();
108
109 let res = client
110 .get(SETTING_ENDPOINTS)
111 .header("authorization", &token)
112 .send()
113 .await?;
114
115 handle_status_error(res.status().as_u16())?;
116
117 let settings: serde_json::Value = serde_json::from_str(&res.text().await?)?;
119
120 let mut body = HashMap::new();
121 let show_current_game = &settings["show_current_game"];
122
123 body.insert("show_current_game", !show_current_game.as_bool().unwrap());
124
125 let res = client
126 .patch(SETTING_ENDPOINTS)
127 .header("authorization", &token)
128 .json(&body)
129 .send()
130 .await?;
131
132 if let Err(err) = handle_status_error(res.status().as_u16()) {
133 Err(err)
134 } else {
135 let current_settings: serde_json::Value = serde_json::from_str(&res.text().await?)?;
136
137 Ok(current_settings["show_current_game"].clone())
138 }
139}
140
141#[tokio::test]
142#[should_panic(expected = "Unauthorized request")]
143async fn unauthorized_request_game() {
144 let discord_token = String::from("Invalid token peko");
145
146 match toggle_game_rpc(discord_token).await {
147 Ok(_) => {
148 panic!("Oh no.");
150 }
151 Err(_) => {
152 panic!("Unauthorized request");
153 }
154 }
155}
156
157#[tokio::test]
158#[should_panic(expected = "Unauthorized request")]
159async fn unauthorized_request_spotify() {
160 let discord_token = String::from("Invalid token peko");
161
162 match toggle_spotify_rpc(discord_token).await {
163 Ok(_) => {
164 panic!("Oh no.");
166 }
167 Err(_) => {
168 panic!("Unauthorized request");
169 }
170 }
171}