From a445341e689ee7f559d6dde6e330fd398cf97b25 Mon Sep 17 00:00:00 2001
From: Flori <39242991+bitfl0wer@users.noreply.github.com>
Date: Mon, 22 May 2023 22:21:27 +0200
Subject: [PATCH 01/23] Update README.md
---
README.md | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 2865df4..59cc210 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,10 @@
-[![Rust]][Rust-url]
+[![Discord]][Discord-invite]
[![Build][build-shield]][build-url]
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
-[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
-[![GPL3 License][license-shield]][license-url]
@@ -54,15 +52,17 @@ Core functionality:
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
[Rust-url]: https://www.rust-lang.org/
-[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/rust.yml?style=plastic
+[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/rust.yml?style=flat
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/rust.yml
-[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=plastic
+[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
-[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=plastic
+[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
-[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=plastic
+[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
[stars-url]: https://github.com/polyphony-chat/chorus/stargazers
-[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=plastic
+[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat
[issues-url]: https://github.com/polyphony-chat/chorus/issues
-[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=plastic
+[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
+[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
+[Discord-invite]: https://discord.com/invite/m3FpcapGDD
From 69425f18acb095597688e32f3562ceaf072c5460 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Mon, 22 May 2023 23:22:34 +0200
Subject: [PATCH 02/23] start working on channel create
---
src/api/guilds/guilds.rs | 14 +++++++++++++-
src/api/schemas.rs | 25 +++++++++++++++++++++++++
2 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index 5bbbd23..0e0ee1a 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -5,7 +5,7 @@ use crate::api::schemas;
use crate::api::types;
use crate::errors::InstanceServerError;
-impl<'a> types::Guild {
+impl types::Guild {
/// Creates a new guild with the given parameters.
///
/// # Arguments
@@ -113,4 +113,16 @@ impl<'a> types::Guild {
None
}
}
+
+ pub async fn create_channel() {}
+}
+
+impl types::Channel {
+ pub async fn create(
+ token: &str,
+ url_api: &str,
+ guild_id: &str,
+ schema: schemas::ChannelCreateSchema,
+ ) {
+ }
}
diff --git a/src/api/schemas.rs b/src/api/schemas.rs
index e84abdf..fb1a14f 100644
--- a/src/api/schemas.rs
+++ b/src/api/schemas.rs
@@ -314,6 +314,31 @@ pub struct UserModifySchema {
pub discriminator: Option,
}
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+// TODO: Implement in polyphony/types
+pub struct ChannelCreateSchema {
+ pub name: String,
+ #[serde(rename = "type")]
+ pub channel_type: Option,
+ pub topic: Option,
+ pub icon: Option,
+ pub bitrate: Option,
+ pub user_limit: Option,
+ pub rate_limit_per_user: Option,
+ pub position: Option,
+ pub permission_overwrites: Option>,
+ pub parent_id: Option,
+ pub id: Option,
+ pub nsfw: Option,
+ pub rtc_region: Option,
+ pub default_auto_archive_duration: Option,
+ pub default_reaction_emoji: Option,
+ pub flags: Option,
+ pub default_thread_rate_limit_per_user: Option,
+ pub video_quality_mode: Option,
+}
+
// I know that some of these tests are... really really basic and unneccessary, but sometimes, I
// just feel like writing tests, so there you go :) -@bitfl0wer
#[cfg(test)]
From fc55a53ed5da271ab3ab22deaac91f63b3882b77 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 13:06:09 +0200
Subject: [PATCH 03/23] Implement create channel methods
---
src/api/guilds/guilds.rs | 66 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index 0e0ee1a..04a0546 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -1,9 +1,12 @@
+use reqwest::Client;
use serde_json::from_str;
use serde_json::to_string;
+use crate::api::limits::Limits;
use crate::api::schemas;
use crate::api::types;
use crate::errors::InstanceServerError;
+use crate::limit::LimitedRequester;
impl types::Guild {
/// Creates a new guild with the given parameters.
@@ -114,15 +117,74 @@ impl types::Guild {
}
}
- pub async fn create_channel() {}
+ /// Sends a request to create a new channel in the guild.
+ ///
+ /// # Arguments
+ ///
+ /// * `url_api` - The base URL for the Discord API.
+ /// * `token` - A Discord bot token.
+ /// * `schema` - A `ChannelCreateSchema` struct containing the properties of the new channel.
+ /// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
+ /// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing a `reqwest::Response` if the request was successful, or an `InstanceServerError` if there was an error.
+ pub async fn create_channel(
+ &self,
+ url_api: &str,
+ token: &str,
+ schema: schemas::ChannelCreateSchema,
+ limits_user: &mut Limits,
+ limits_instance: &mut Limits,
+ ) -> Result {
+ types::Channel::create(
+ token,
+ url_api,
+ &self.id,
+ schema,
+ limits_user,
+ limits_instance,
+ )
+ .await
+ }
}
impl types::Channel {
+ /// Sends a request to create a new channel in a guild.
+ ///
+ /// # Arguments
+ ///
+ /// * `token` - A Discord bot token.
+ /// * `url_api` - The base URL for the Discord API.
+ /// * `guild_id` - The ID of the guild where the channel will be created.
+ /// * `schema` - A `ChannelCreateSchema` struct containing the properties of the new channel.
+ /// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
+ /// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing a `reqwest::Response` if the request was successful, or an `InstanceServerError` if there was an error.
pub async fn create(
token: &str,
url_api: &str,
guild_id: &str,
schema: schemas::ChannelCreateSchema,
- ) {
+ limits_user: &mut Limits,
+ limits_instance: &mut Limits,
+ ) -> Result {
+ let request = Client::new()
+ .post(format!("{}/guilds/{}/channels/", url_api, guild_id))
+ .bearer_auth(token)
+ .body(to_string(&schema).unwrap());
+ let mut requester = LimitedRequester::new().await;
+ requester
+ .send_request(
+ request,
+ crate::api::limits::LimitType::Guild,
+ limits_instance,
+ limits_user,
+ )
+ .await
}
}
From 12095132d59bb2e4ab663825344dc029c23b0ecd Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 13:15:57 +0200
Subject: [PATCH 04/23] Add instance, guild, channel to TestBundle
---
tests/integration.rs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tests/integration.rs b/tests/integration.rs
index e004496..0d5389c 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1,5 +1,5 @@
use chorus::{
- api::{AuthUsername, RegisterSchema, User},
+ api::{AuthUsername, Channel, Guild, RegisterSchema, User},
instance::Instance,
URLBundle,
};
@@ -8,6 +8,9 @@ use chorus::{
struct TestBundle {
urls: URLBundle,
user: User,
+ instance: Instance,
+ guild: Guild,
+ channel: Channel,
}
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
From 81885cffc025756746d1d0df07c8812da000be67 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 13:50:39 +0200
Subject: [PATCH 05/23] Change create channel method to return Channel
Previously returned a Response object.
---
src/api/guilds/guilds.rs | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index 04a0546..3761140 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -137,7 +137,7 @@ impl types::Guild {
schema: schemas::ChannelCreateSchema,
limits_user: &mut Limits,
limits_instance: &mut Limits,
- ) -> Result {
+ ) -> Result {
types::Channel::create(
token,
url_api,
@@ -172,13 +172,13 @@ impl types::Channel {
schema: schemas::ChannelCreateSchema,
limits_user: &mut Limits,
limits_instance: &mut Limits,
- ) -> Result {
+ ) -> Result {
let request = Client::new()
.post(format!("{}/guilds/{}/channels/", url_api, guild_id))
.bearer_auth(token)
.body(to_string(&schema).unwrap());
let mut requester = LimitedRequester::new().await;
- requester
+ let result = match requester
.send_request(
request,
crate::api::limits::LimitType::Guild,
@@ -186,5 +186,16 @@ impl types::Channel {
limits_user,
)
.await
+ {
+ Ok(result) => result,
+ Err(e) => return Err(e),
+ };
+ match from_str::(&result.text().await.unwrap()) {
+ Ok(object) => Ok(object),
+ Err(e) => Err(InstanceServerError::RequestErrorError {
+ url: url_api.to_string(),
+ error: e.to_string(),
+ }),
+ }
}
}
From b05c5b122480d6a99410579ca02e12d88979cb11 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 13:57:02 +0200
Subject: [PATCH 06/23] Impl. instance, guild_id, channel to TestBundle
---
tests/integration.rs | 57 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 53 insertions(+), 4 deletions(-)
diff --git a/tests/integration.rs b/tests/integration.rs
index 0d5389c..79e1d2a 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1,5 +1,6 @@
use chorus::{
- api::{AuthUsername, Channel, Guild, RegisterSchema, User},
+ api::schemas,
+ api::{AuthUsername, Channel, Guild, GuildCreateSchema, RegisterSchema, User},
instance::Instance,
URLBundle,
};
@@ -9,7 +10,7 @@ struct TestBundle {
urls: URLBundle,
user: User,
instance: Instance,
- guild: Guild,
+ guild_id: String,
channel: Channel,
}
@@ -35,9 +36,57 @@ async fn setup() -> TestBundle {
None,
)
.unwrap();
- let user = instance.register_account(®).await.unwrap();
+ let guild_create_schema = GuildCreateSchema {
+ name: Some("Test-Guild!".to_string()),
+ region: None,
+ icon: None,
+ channels: None,
+ guild_template_code: None,
+ system_channel_id: None,
+ rules_channel_id: None,
+ };
+ let channel_create_schema = schemas::ChannelCreateSchema {
+ name: "testchannel".to_string(),
+ channel_type: Some(0),
+ topic: None,
+ icon: None,
+ bitrate: None,
+ user_limit: None,
+ rate_limit_per_user: None,
+ position: None,
+ permission_overwrites: None,
+ parent_id: None,
+ id: None,
+ nsfw: None,
+ rtc_region: None,
+ default_auto_archive_duration: None,
+ default_reaction_emoji: None,
+ flags: None,
+ default_thread_rate_limit_per_user: None,
+ video_quality_mode: None,
+ };
+ let mut user = instance.register_account(®).await.unwrap();
+ let guild_id = Guild::create(&mut user, urls.get_api(), guild_create_schema)
+ .await
+ .unwrap();
+ let channel = Channel::create(
+ &user.token,
+ urls.get_api(),
+ guild_id.as_str(),
+ channel_create_schema,
+ &mut user.limits,
+ &mut instance.limits,
+ )
+ .await
+ .unwrap();
- TestBundle { urls, user }
+ TestBundle {
+ urls,
+ user,
+ instance,
+ guild_id,
+ channel,
+ }
}
// Teardown method to clean up after a test.
From d48644281381f47bfa3227075e0600e727058e04 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:00:08 +0200
Subject: [PATCH 07/23] Delete Guild on test cleanup
---
tests/integration.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tests/integration.rs b/tests/integration.rs
index 79e1d2a..f84fa48 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -90,7 +90,13 @@ async fn setup() -> TestBundle {
}
// Teardown method to clean up after a test.
-async fn teardown(bundle: TestBundle) {
+async fn teardown(mut bundle: TestBundle) {
+ Guild::delete(
+ &mut bundle.user,
+ bundle.instance.urls.get_api(),
+ bundle.guild_id,
+ )
+ .await;
bundle.user.delete().await;
}
From 985e180c65af990bec58647b3d5cdd658d347fc5 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:28:45 +0200
Subject: [PATCH 08/23] Add teardown() call to guild_creation test
---
tests/integration.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/integration.rs b/tests/integration.rs
index f84fa48..7cf76cf 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -128,5 +128,6 @@ mod guild {
None => assert!(true),
Some(_) => assert!(false),
}
+ crate::teardown(bundle).await
}
}
From fd0442dcc9d647d6338990b9d97c5e341e36c4b7 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:31:21 +0200
Subject: [PATCH 09/23] Start working on get() channel route
---
src/api/guilds/guilds.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index 3761140..40b22d8 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -198,4 +198,6 @@ impl types::Channel {
}),
}
}
+
+ pub async fn get() {}
}
From 9a6a08bb671828fbe3206aa737fa6d0e20a2cc98 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:37:34 +0200
Subject: [PATCH 10/23] Implement get() for channel
---
src/api/guilds/guilds.rs | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index 40b22d8..a2fc119 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -199,5 +199,35 @@ impl types::Channel {
}
}
- pub async fn get() {}
+ pub async fn get(
+ token: &str,
+ url_api: &str,
+ guild_id: &str,
+ limits_user: &mut Limits,
+ limits_instance: &mut Limits,
+ ) -> Result {
+ let request = Client::new()
+ .get(format!("{}/guilds/{}/channels/", url_api, guild_id))
+ .bearer_auth(token);
+ let mut requester = LimitedRequester::new().await;
+ let result = match requester
+ .send_request(
+ request,
+ crate::api::limits::LimitType::Guild,
+ limits_instance,
+ limits_user,
+ )
+ .await
+ {
+ Ok(result) => result,
+ Err(e) => return Err(e),
+ };
+ match from_str::(&result.text().await.unwrap()) {
+ Ok(object) => Ok(object),
+ Err(e) => Err(InstanceServerError::RequestErrorError {
+ url: url_api.to_string(),
+ error: e.to_string(),
+ }),
+ }
+ }
}
From 912d8043d0b54ecd34d063c802c167039c732e2a Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:38:03 +0200
Subject: [PATCH 11/23] Replace URL in errors with full route URL
---
src/api/guilds/guilds.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index a2fc119..da7c503 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -193,7 +193,7 @@ impl types::Channel {
match from_str::(&result.text().await.unwrap()) {
Ok(object) => Ok(object),
Err(e) => Err(InstanceServerError::RequestErrorError {
- url: url_api.to_string(),
+ url: format!("{}/guilds/{}/channels/", url_api, guild_id),
error: e.to_string(),
}),
}
@@ -225,7 +225,7 @@ impl types::Channel {
match from_str::(&result.text().await.unwrap()) {
Ok(object) => Ok(object),
Err(e) => Err(InstanceServerError::RequestErrorError {
- url: url_api.to_string(),
+ url: format!("{}/guilds/{}/channels/", url_api, guild_id),
error: e.to_string(),
}),
}
From b293f5ed96c26820c1fcd51418adae6b34d60d67 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:50:48 +0200
Subject: [PATCH 12/23] Add ChannelType enum
---
src/api/types.rs | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/src/api/types.rs b/src/api/types.rs
index 5631130..e5cc3de 100644
--- a/src/api/types.rs
+++ b/src/api/types.rs
@@ -544,7 +544,7 @@ struct ChannelMention {
id: String,
guild_id: String,
#[serde(rename = "type")]
- channel_type: i32,
+ channel_type: ChannelType,
name: String,
}
@@ -742,7 +742,7 @@ pub struct GuildMember {
pub struct Channel {
pub id: String,
#[serde(rename = "type")]
- pub channel_type: i32,
+ pub channel_type: ChannelType,
pub guild_id: Option,
pub position: Option,
pub permission_overwrites: Option>,
@@ -1480,3 +1480,21 @@ pub struct Webhook {
pub struct GuildCreateResponse {
pub id: String,
}
+
+#[derive(Serialize, Deserialize, Debug, Default, Clone)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum ChannelType {
+ #[default]
+ GuildText = 0,
+ DM = 1,
+ GuildVoice = 2,
+ GroupDM = 3,
+ GuildCategory = 4,
+ GuildAnnouncement = 5,
+ AnnouncementThread = 10,
+ PublicThread = 11,
+ PrivateThread = 12,
+ GuildStageVoice = 13,
+ GuildDirectory = 14,
+ GuildForum = 15,
+}
From 64693c87706faebaa362513167165eb61aee254c Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 14:51:28 +0200
Subject: [PATCH 13/23] Add todo
---
src/api/types.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/api/types.rs b/src/api/types.rs
index e5cc3de..5ca4f26 100644
--- a/src/api/types.rs
+++ b/src/api/types.rs
@@ -1483,6 +1483,7 @@ pub struct GuildCreateResponse {
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+// TODO: Implement in polyphony/types
pub enum ChannelType {
#[default]
GuildText = 0,
From 420f3798f469aae893b875406b9667ada08e3013 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 15:10:56 +0200
Subject: [PATCH 14/23] try flattening the enum to fix CI
---
src/api/types.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/api/types.rs b/src/api/types.rs
index 5ca4f26..4e76c8b 100644
--- a/src/api/types.rs
+++ b/src/api/types.rs
@@ -544,6 +544,7 @@ struct ChannelMention {
id: String,
guild_id: String,
#[serde(rename = "type")]
+ #[serde(flatten)]
channel_type: ChannelType,
name: String,
}
@@ -742,6 +743,7 @@ pub struct GuildMember {
pub struct Channel {
pub id: String,
#[serde(rename = "type")]
+ #[serde(flatten)]
pub channel_type: ChannelType,
pub guild_id: Option,
pub position: Option,
From f4f01a00ccf404ecb41e55996168aad17b41f8ab Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 15:17:04 +0200
Subject: [PATCH 15/23] Revert "Add ChannelType enum"
This reverts commit b293f5ed96c26820c1fcd51418adae6b34d60d67.
---
src/api/types.rs | 25 ++-----------------------
1 file changed, 2 insertions(+), 23 deletions(-)
diff --git a/src/api/types.rs b/src/api/types.rs
index 4e76c8b..5631130 100644
--- a/src/api/types.rs
+++ b/src/api/types.rs
@@ -544,8 +544,7 @@ struct ChannelMention {
id: String,
guild_id: String,
#[serde(rename = "type")]
- #[serde(flatten)]
- channel_type: ChannelType,
+ channel_type: i32,
name: String,
}
@@ -743,8 +742,7 @@ pub struct GuildMember {
pub struct Channel {
pub id: String,
#[serde(rename = "type")]
- #[serde(flatten)]
- pub channel_type: ChannelType,
+ pub channel_type: i32,
pub guild_id: Option,
pub position: Option,
pub permission_overwrites: Option>,
@@ -1482,22 +1480,3 @@ pub struct Webhook {
pub struct GuildCreateResponse {
pub id: String,
}
-
-#[derive(Serialize, Deserialize, Debug, Default, Clone)]
-#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
-// TODO: Implement in polyphony/types
-pub enum ChannelType {
- #[default]
- GuildText = 0,
- DM = 1,
- GuildVoice = 2,
- GroupDM = 3,
- GuildCategory = 4,
- GuildAnnouncement = 5,
- AnnouncementThread = 10,
- PublicThread = 11,
- PrivateThread = 12,
- GuildStageVoice = 13,
- GuildDirectory = 14,
- GuildForum = 15,
-}
From 368152e09279d866741a51676833989d1903b599 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 15:25:32 +0200
Subject: [PATCH 16/23] Make Channel type derive Eq, PartialEq recursively
---
src/api/types.rs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/api/types.rs b/src/api/types.rs
index 5631130..f040dd2 100644
--- a/src/api/types.rs
+++ b/src/api/types.rs
@@ -319,7 +319,7 @@ pub struct RoleObject {
//pub tags: Option
}
-#[derive(Serialize, Deserialize, Debug, Default, Clone)]
+#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
pub struct UserObject {
pub id: String,
username: String,
@@ -722,7 +722,7 @@ pub struct MessageInteraction {
pub member: Option,
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct GuildMember {
pub user: Option,
pub nick: Option,
@@ -738,7 +738,7 @@ pub struct GuildMember {
pub communication_disabled_until: Option,
}
-#[derive(Default, Debug, Serialize, Deserialize, Clone)]
+#[derive(Default, Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub struct Channel {
pub id: String,
#[serde(rename = "type")]
@@ -777,7 +777,7 @@ pub struct Channel {
pub default_forum_layout: Option,
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct Tag {
pub id: u64,
pub name: String,
@@ -786,7 +786,7 @@ pub struct Tag {
pub emoji_name: Option,
}
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub struct PermissionOverwrite {
pub id: String,
#[serde(rename = "type")]
@@ -795,7 +795,7 @@ pub struct PermissionOverwrite {
pub deny: String,
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct ThreadMetadata {
pub archived: bool,
pub auto_archive_duration: i32,
@@ -805,7 +805,7 @@ pub struct ThreadMetadata {
pub create_timestamp: Option,
}
-#[derive(Default, Debug, Deserialize, Serialize, Clone)]
+#[derive(Default, Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct ThreadMember {
pub id: Option,
pub user_id: Option,
@@ -855,7 +855,7 @@ pub struct StageInstance {
pub guild_scheduled_event_id: Option,
}
-#[derive(Debug, Deserialize, Serialize, Clone)]
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct DefaultReaction {
pub emoji_id: Option,
pub emoji_name: Option,
From 797af67d3a53661705d10b0d9d465b0d41c420f3 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 15:25:47 +0200
Subject: [PATCH 17/23] Add get_channel integration test
---
tests/integration.rs | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/tests/integration.rs b/tests/integration.rs
index 7cf76cf..c30c3ad 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -101,7 +101,7 @@ async fn teardown(mut bundle: TestBundle) {
}
mod guild {
- use chorus::api::{schemas, types};
+ use chorus::api::{schemas, types, Channel};
#[tokio::test]
async fn guild_creation_deletion() {
@@ -130,4 +130,25 @@ mod guild {
}
crate::teardown(bundle).await
}
+
+ #[tokio::test]
+ async fn get_channel() {
+ let mut bundle = crate::setup().await;
+ let bundle_channel = bundle.channel.clone();
+ let bundle_user = &mut bundle.user;
+
+ assert_eq!(
+ bundle_channel,
+ Channel::get(
+ bundle_user.token.as_str(),
+ bundle.instance.urls.get_api(),
+ bundle.guild_id.as_str(),
+ &mut bundle_user.limits,
+ &mut bundle.instance.limits
+ )
+ .await
+ .unwrap()
+ );
+ crate::teardown(bundle).await
+ }
}
From 8136bfa9a5daa3530edef34467d43ba350c78430 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 16:25:08 +0200
Subject: [PATCH 18/23] Fix: Used wrong rout to retrieve singular channel
---
src/api/guilds/guilds.rs | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index da7c503..caeea3b 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -202,12 +202,12 @@ impl types::Channel {
pub async fn get(
token: &str,
url_api: &str,
- guild_id: &str,
+ channel_id: &str,
limits_user: &mut Limits,
limits_instance: &mut Limits,
) -> Result {
let request = Client::new()
- .get(format!("{}/guilds/{}/channels/", url_api, guild_id))
+ .get(format!("{}/channels/{}/", url_api, channel_id))
.bearer_auth(token);
let mut requester = LimitedRequester::new().await;
let result = match requester
@@ -222,10 +222,11 @@ impl types::Channel {
Ok(result) => result,
Err(e) => return Err(e),
};
- match from_str::(&result.text().await.unwrap()) {
+ let result_text = result.text().await.unwrap();
+ match from_str::(&result_text) {
Ok(object) => Ok(object),
Err(e) => Err(InstanceServerError::RequestErrorError {
- url: format!("{}/guilds/{}/channels/", url_api, guild_id),
+ url: format!("{}/channels/{}/", url_api, channel_id),
error: e.to_string(),
}),
}
From 3f35c9d74c2f05b546da91b98e7cad3d46672d67 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 16:25:31 +0200
Subject: [PATCH 19/23] Fix get_channel test
---
tests/integration.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/integration.rs b/tests/integration.rs
index c30c3ad..045dc08 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -57,12 +57,12 @@ async fn setup() -> TestBundle {
permission_overwrites: None,
parent_id: None,
id: None,
- nsfw: None,
+ nsfw: Some(false),
rtc_region: None,
default_auto_archive_duration: None,
default_reaction_emoji: None,
- flags: None,
- default_thread_rate_limit_per_user: None,
+ flags: Some(0),
+ default_thread_rate_limit_per_user: Some(0),
video_quality_mode: None,
};
let mut user = instance.register_account(®).await.unwrap();
@@ -142,7 +142,7 @@ mod guild {
Channel::get(
bundle_user.token.as_str(),
bundle.instance.urls.get_api(),
- bundle.guild_id.as_str(),
+ &bundle_channel.id,
&mut bundle_user.limits,
&mut bundle.instance.limits
)
From 44f9f2cae5f688e1d7ae1679a046a95f08696795 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 16:28:09 +0200
Subject: [PATCH 20/23] Create channels.rs
---
src/api/channels/mod.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/api/channels/mod.rs b/src/api/channels/mod.rs
index 8f05488..72f4e3e 100644
--- a/src/api/channels/mod.rs
+++ b/src/api/channels/mod.rs
@@ -1,3 +1,5 @@
+pub mod channels;
pub mod messages;
+pub use channels::*;
pub use messages::*;
From 7493597f4f8bd61afa7bb7fe2967b600cd6d6319 Mon Sep 17 00:00:00 2001
From: bitfl0wer
Date: Tue, 23 May 2023 16:28:25 +0200
Subject: [PATCH 21/23] Move Channels::get() to channels.rs
---
src/api/channels/channels.rs | 43 ++++++++++++++++++++++++++++++++++++
src/api/guilds/guilds.rs | 33 ---------------------------
2 files changed, 43 insertions(+), 33 deletions(-)
create mode 100644 src/api/channels/channels.rs
diff --git a/src/api/channels/channels.rs b/src/api/channels/channels.rs
new file mode 100644
index 0000000..51552a1
--- /dev/null
+++ b/src/api/channels/channels.rs
@@ -0,0 +1,43 @@
+use reqwest::Client;
+use serde_json::from_str;
+
+use crate::{
+ api::{limits::Limits, types},
+ errors::InstanceServerError,
+ limit::LimitedRequester,
+};
+
+impl types::Channel {
+ pub async fn get(
+ token: &str,
+ url_api: &str,
+ channel_id: &str,
+ limits_user: &mut Limits,
+ limits_instance: &mut Limits,
+ ) -> Result {
+ let request = Client::new()
+ .get(format!("{}/channels/{}/", url_api, channel_id))
+ .bearer_auth(token);
+ let mut requester = LimitedRequester::new().await;
+ let result = match requester
+ .send_request(
+ request,
+ crate::api::limits::LimitType::Guild,
+ limits_instance,
+ limits_user,
+ )
+ .await
+ {
+ Ok(result) => result,
+ Err(e) => return Err(e),
+ };
+ let result_text = result.text().await.unwrap();
+ match from_str::(&result_text) {
+ Ok(object) => Ok(object),
+ Err(e) => Err(InstanceServerError::RequestErrorError {
+ url: format!("{}/channels/{}/", url_api, channel_id),
+ error: e.to_string(),
+ }),
+ }
+ }
+}
diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs
index caeea3b..21140f1 100644
--- a/src/api/guilds/guilds.rs
+++ b/src/api/guilds/guilds.rs
@@ -198,37 +198,4 @@ impl types::Channel {
}),
}
}
-
- pub async fn get(
- token: &str,
- url_api: &str,
- channel_id: &str,
- limits_user: &mut Limits,
- limits_instance: &mut Limits,
- ) -> Result {
- let request = Client::new()
- .get(format!("{}/channels/{}/", url_api, channel_id))
- .bearer_auth(token);
- let mut requester = LimitedRequester::new().await;
- let result = match requester
- .send_request(
- request,
- crate::api::limits::LimitType::Guild,
- limits_instance,
- limits_user,
- )
- .await
- {
- Ok(result) => result,
- Err(e) => return Err(e),
- };
- let result_text = result.text().await.unwrap();
- match from_str::(&result_text) {
- Ok(object) => Ok(object),
- Err(e) => Err(InstanceServerError::RequestErrorError {
- url: format!("{}/channels/{}/", url_api, channel_id),
- error: e.to_string(),
- }),
- }
- }
}
From 0daf0b8a3d33b20ca8949d13520867e8de119b34 Mon Sep 17 00:00:00 2001
From: Flori <39242991+bitfl0wer@users.noreply.github.com>
Date: Wed, 24 May 2023 11:12:51 +0200
Subject: [PATCH 22/23] Delete images directory
---
images/polyphony-chorus-round-light.png | Bin 2031563 -> 0 bytes
images/polyphony-chorus-round.png | Bin 1886731 -> 0 bytes
images/polyphony-chorus.png | Bin 1494645 -> 0 bytes
images/polyphony-chorus8bit.png | Bin 344678 -> 0 bytes
4 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 images/polyphony-chorus-round-light.png
delete mode 100644 images/polyphony-chorus-round.png
delete mode 100644 images/polyphony-chorus.png
delete mode 100644 images/polyphony-chorus8bit.png
diff --git a/images/polyphony-chorus-round-light.png b/images/polyphony-chorus-round-light.png
deleted file mode 100644
index 000cbf021c05434071a4abff91401f71515044eb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2031563
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