use crate::models::{HasId, HasName, Snowflake};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Channel {
pub id: Option<Snowflake>,
pub guild_id: Option<Snowflake>,
pub name: Option<String>,
#[serde(rename = "type")]
pub channel_type: Option<ChannelType>,
pub sub_type: Option<ChannelSubType>,
pub position: Option<i32>,
pub parent_id: Option<Snowflake>,
pub owner_id: Option<Snowflake>,
pub private_type: Option<PrivateType>,
pub private_user_ids: Option<Vec<String>>,
pub speak_permission: Option<SpeakPermission>,
pub application_id: Option<Snowflake>,
pub permissions: Option<String>,
pub op_user_id: Option<Snowflake>,
}
impl Channel {
pub fn new() -> Self {
Self {
id: None,
guild_id: None,
name: None,
channel_type: None,
sub_type: None,
position: None,
parent_id: None,
owner_id: None,
private_type: None,
private_user_ids: None,
speak_permission: None,
application_id: None,
permissions: None,
op_user_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, id: String, data: serde_json::Value) -> Self {
Self {
id: Some(id),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
name: data.get("name").and_then(|v| v.as_str()).map(String::from),
channel_type: data
.get("type")
.and_then(|v| v.as_u64())
.and_then(|v| ChannelType::from_u8(v as u8)),
sub_type: data
.get("sub_type")
.and_then(|v| v.as_u64())
.and_then(|v| ChannelSubType::from_u8(v as u8)),
position: data
.get("position")
.and_then(|v| v.as_i64())
.map(|v| v as i32),
parent_id: data
.get("parent_id")
.and_then(|v| v.as_str())
.map(String::from),
owner_id: data
.get("owner_id")
.and_then(|v| v.as_str())
.map(String::from),
private_type: data
.get("private_type")
.and_then(|v| v.as_u64())
.and_then(|v| PrivateType::from_u8(v as u8)),
private_user_ids: data.get("private_user_ids").and_then(|v| {
v.as_array().map(|values| {
values
.iter()
.filter_map(|value| value.as_str().map(String::from))
.collect()
})
}),
speak_permission: data
.get("speak_permission")
.and_then(|v| v.as_u64())
.and_then(|v| SpeakPermission::from_u8(v as u8)),
application_id: data
.get("application_id")
.and_then(|v| v.as_str())
.map(String::from),
permissions: data
.get("permissions")
.and_then(|v| v.as_str())
.map(String::from),
op_user_id: data
.get("op_user_id")
.and_then(|v| v.as_str())
.map(String::from),
}
}
pub fn mention(&self) -> String {
format!("<#{}>", self.id.as_ref().unwrap_or(&String::new()))
}
pub fn is_text(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Text))
}
pub fn is_voice(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Voice))
}
pub fn is_group(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Group))
}
pub fn is_live(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Live))
}
pub fn is_application(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Application))
}
pub fn is_discussion(&self) -> bool {
matches!(self.channel_type, Some(ChannelType::Discussion))
}
pub fn is_public(&self) -> bool {
matches!(self.private_type, Some(PrivateType::Public) | None)
}
pub fn is_admin_only(&self) -> bool {
matches!(self.private_type, Some(PrivateType::AdminOnly))
}
pub fn is_specified_users_only(&self) -> bool {
matches!(
self.private_type,
Some(PrivateType::AdminAndSpecifiedMembers)
)
}
pub fn everyone_can_speak(&self) -> bool {
matches!(self.speak_permission, Some(SpeakPermission::Everyone))
}
pub fn admin_only_speak(&self) -> bool {
matches!(
self.speak_permission,
Some(SpeakPermission::AdminAndSpecifiedMembers)
)
}
pub fn display_name(&self) -> Option<&str> {
self.name.as_deref()
}
}
impl Default for Channel {
fn default() -> Self {
Self::new()
}
}
impl HasId for Channel {
fn id(&self) -> Option<&Snowflake> {
self.id.as_ref()
}
}
impl HasName for Channel {
fn name(&self) -> &str {
self.name.as_deref().unwrap_or("")
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct ChannelValueObject {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub channel_type: Option<ChannelType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<Snowflake>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_id: Option<Snowflake>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_type: Option<ChannelSubType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub private_type: Option<PrivateType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub private_user_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speak_permission: Option<SpeakPermission>,
#[serde(skip_serializing_if = "Option::is_none")]
pub application_id: Option<Snowflake>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub op_user_id: Option<Snowflake>,
}
impl ChannelValueObject {
pub fn new(
name: impl Into<String>,
channel_type: ChannelType,
sub_type: ChannelSubType,
) -> Self {
Self {
name: Some(name.into()),
channel_type: Some(channel_type),
sub_type: Some(sub_type),
..Default::default()
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u32", into = "u32")]
#[repr(u32)]
pub enum ChannelType {
Text = 0,
Voice = 2,
Group = 4,
Live = 10005,
Application = 10006,
Discussion = 10007,
Unknown(u32),
}
pub const CHANNEL_TYPE_TEXT: ChannelType = ChannelType::Text;
pub const CHANNEL_TYPE_VOICE: ChannelType = ChannelType::Voice;
pub const CHANNEL_TYPE_CATEGORY: ChannelType = ChannelType::Group;
pub const CHANNEL_TYPE_LIVE: ChannelType = ChannelType::Live;
pub const CHANNEL_TYPE_APPLICATION: ChannelType = ChannelType::Application;
pub const CHANNEL_TYPE_FORUM: ChannelType = ChannelType::Discussion;
#[allow(non_upper_case_globals)]
pub const ChannelTypeText: ChannelType = CHANNEL_TYPE_TEXT;
#[allow(non_upper_case_globals)]
pub const ChannelTypeVoice: ChannelType = CHANNEL_TYPE_VOICE;
#[allow(non_upper_case_globals)]
pub const ChannelTypeCategory: ChannelType = CHANNEL_TYPE_CATEGORY;
#[allow(non_upper_case_globals)]
pub const ChannelTypeLive: ChannelType = CHANNEL_TYPE_LIVE;
#[allow(non_upper_case_globals)]
pub const ChannelTypeApplication: ChannelType = CHANNEL_TYPE_APPLICATION;
#[allow(non_upper_case_globals)]
pub const ChannelTypeForum: ChannelType = CHANNEL_TYPE_FORUM;
impl From<u32> for ChannelType {
fn from(value: u32) -> Self {
match value {
0 => Self::Text,
2 => Self::Voice,
4 => Self::Group,
10005 => Self::Live,
10006 => Self::Application,
10007 => Self::Discussion,
other => Self::Unknown(other),
}
}
}
impl ChannelType {
pub fn from_u8(value: u8) -> Option<Self> {
Some(Self::from(value as u32))
}
}
impl From<ChannelType> for u32 {
fn from(channel_type: ChannelType) -> Self {
match channel_type {
ChannelType::Text => 0,
ChannelType::Voice => 2,
ChannelType::Group => 4,
ChannelType::Live => 10005,
ChannelType::Application => 10006,
ChannelType::Discussion => 10007,
ChannelType::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u32", into = "u32")]
#[repr(u32)]
pub enum ChannelSubType {
Talk = 0,
Post = 1,
Cheat = 2,
Black = 3,
Unknown(u32),
}
pub const CHANNEL_SUB_TYPE_CHAT: ChannelSubType = ChannelSubType::Talk;
pub const CHANNEL_SUB_TYPE_NOTICE: ChannelSubType = ChannelSubType::Post;
pub const CHANNEL_SUB_TYPE_GUIDE: ChannelSubType = ChannelSubType::Cheat;
pub const CHANNEL_SUB_TYPE_TEAM_GAME: ChannelSubType = ChannelSubType::Black;
#[allow(non_upper_case_globals)]
pub const ChannelSubTypeChat: ChannelSubType = CHANNEL_SUB_TYPE_CHAT;
#[allow(non_upper_case_globals)]
pub const ChannelSubTypeNotice: ChannelSubType = CHANNEL_SUB_TYPE_NOTICE;
#[allow(non_upper_case_globals)]
pub const ChannelSubTypeGuide: ChannelSubType = CHANNEL_SUB_TYPE_GUIDE;
#[allow(non_upper_case_globals)]
pub const ChannelSubTypeTeamGame: ChannelSubType = CHANNEL_SUB_TYPE_TEAM_GAME;
impl From<u32> for ChannelSubType {
fn from(value: u32) -> Self {
match value {
0 => Self::Talk,
1 => Self::Post,
2 => Self::Cheat,
3 => Self::Black,
other => Self::Unknown(other),
}
}
}
impl ChannelSubType {
pub fn from_u8(value: u8) -> Option<Self> {
Some(Self::from(value as u32))
}
}
impl From<ChannelSubType> for u32 {
fn from(subtype: ChannelSubType) -> Self {
match subtype {
ChannelSubType::Talk => 0,
ChannelSubType::Post => 1,
ChannelSubType::Cheat => 2,
ChannelSubType::Black => 3,
ChannelSubType::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u8", into = "u8")]
#[repr(u8)]
pub enum PrivateType {
Public = 0,
AdminOnly = 1,
AdminAndSpecifiedMembers = 2,
Unknown(u8),
}
pub type ChannelPrivateType = PrivateType;
pub const CHANNEL_PRIVATE_TYPE_PUBLIC: ChannelPrivateType = PrivateType::Public;
pub const CHANNEL_PRIVATE_TYPE_ONLY_ADMIN: ChannelPrivateType = PrivateType::AdminOnly;
pub const CHANNEL_PRIVATE_TYPE_ADMIN_AND_MEMBER: ChannelPrivateType =
PrivateType::AdminAndSpecifiedMembers;
#[allow(non_upper_case_globals)]
pub const ChannelPrivateTypePublic: ChannelPrivateType = CHANNEL_PRIVATE_TYPE_PUBLIC;
#[allow(non_upper_case_globals)]
pub const ChannelPrivateTypeOnlyAdmin: ChannelPrivateType = CHANNEL_PRIVATE_TYPE_ONLY_ADMIN;
#[allow(non_upper_case_globals)]
pub const ChannelPrivateTypeAdminAndMember: ChannelPrivateType =
CHANNEL_PRIVATE_TYPE_ADMIN_AND_MEMBER;
impl From<u8> for PrivateType {
fn from(value: u8) -> Self {
match value {
0 => Self::Public,
1 => Self::AdminOnly,
2 => Self::AdminAndSpecifiedMembers,
other => Self::Unknown(other),
}
}
}
impl From<PrivateType> for u8 {
fn from(private_type: PrivateType) -> Self {
match private_type {
PrivateType::Public => 0,
PrivateType::AdminOnly => 1,
PrivateType::AdminAndSpecifiedMembers => 2,
PrivateType::Unknown(other) => other,
}
}
}
impl PrivateType {
pub fn from_u8(value: u8) -> Option<Self> {
Some(Self::from(value))
}
}
impl From<PrivateType> for u32 {
fn from(private_type: PrivateType) -> Self {
match private_type {
PrivateType::Public => 0,
PrivateType::AdminOnly => 1,
PrivateType::AdminAndSpecifiedMembers => 2,
PrivateType::Unknown(value) => value as u32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u8", into = "u8")]
#[repr(u8)]
pub enum SpeakPermission {
Invalid = 0,
Everyone = 1,
AdminAndSpecifiedMembers = 2,
Unknown(u8),
}
pub type SpeakPermissionType = SpeakPermission;
pub const SPEAK_PERMISSION_TYPE_PUBLIC: SpeakPermissionType = SpeakPermission::Everyone;
pub const SPEAK_PERMISSION_TYPE_ADMIN_AND_MEMBER: SpeakPermissionType =
SpeakPermission::AdminAndSpecifiedMembers;
#[allow(non_upper_case_globals)]
pub const SpeakPermissionTypePublic: SpeakPermissionType = SPEAK_PERMISSION_TYPE_PUBLIC;
#[allow(non_upper_case_globals)]
pub const SpeakPermissionTypeAdminAndMember: SpeakPermissionType =
SPEAK_PERMISSION_TYPE_ADMIN_AND_MEMBER;
impl From<u8> for SpeakPermission {
fn from(value: u8) -> Self {
match value {
0 => Self::Invalid,
1 => Self::Everyone,
2 => Self::AdminAndSpecifiedMembers,
other => Self::Unknown(other),
}
}
}
impl From<SpeakPermission> for u8 {
fn from(speak_permission: SpeakPermission) -> Self {
match speak_permission {
SpeakPermission::Invalid => 0,
SpeakPermission::Everyone => 1,
SpeakPermission::AdminAndSpecifiedMembers => 2,
SpeakPermission::Unknown(other) => other,
}
}
}
impl SpeakPermission {
pub fn from_u8(value: u8) -> Option<Self> {
Some(Self::from(value))
}
}
impl From<SpeakPermission> for u32 {
fn from(speak_permission: SpeakPermission) -> Self {
match speak_permission {
SpeakPermission::Invalid => 0,
SpeakPermission::Everyone => 1,
SpeakPermission::AdminAndSpecifiedMembers => 2,
SpeakPermission::Unknown(value) => value as u32,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelPermissions {
pub channel_id: Option<Snowflake>,
pub user_id: Option<Snowflake>,
pub role_id: Option<Snowflake>,
pub permissions: Option<String>,
}
impl ChannelPermissions {
pub fn new() -> Self {
Self {
channel_id: None,
user_id: None,
role_id: None,
permissions: None,
}
}
pub fn is_user_permission(&self) -> bool {
self.user_id.is_some()
}
pub fn is_role_permission(&self) -> bool {
self.role_id.is_some()
}
}
impl Default for ChannelPermissions {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct ChannelRolesPermissions {
pub channel_id: Option<Snowflake>,
pub role_id: Option<Snowflake>,
pub permissions: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct UpdateChannelPermissions {
#[serde(skip_serializing_if = "Option::is_none")]
pub add: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub remove: Option<String>,
}
impl UpdateChannelPermissions {
pub fn new(add: Option<impl ToString>, remove: Option<impl ToString>) -> Self {
Self {
add: add.map(|value| value.to_string()),
remove: remove.map(|value| value.to_string()),
}
}
pub fn validate(&self) -> crate::error::Result<()> {
if let Some(add) = self.add.as_deref() {
add.parse::<u64>().map_err(|err| {
crate::error::BotError::invalid_data(format!("invalid parameter add: {err}"))
})?;
}
if let Some(remove) = self.remove.as_deref() {
remove.parse::<u64>().map_err(|err| {
crate::error::BotError::invalid_data(format!("invalid parameter remove: {err}"))
})?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_creation() {
let channel = Channel::new();
assert!(channel.id.is_none());
assert!(channel.name.is_none());
assert!(channel.is_public()); }
#[test]
fn test_channel_types() {
let mut channel = Channel::new();
channel.channel_type = Some(ChannelType::Text);
assert!(channel.is_text());
assert!(!channel.is_voice());
channel.channel_type = Some(ChannelType::Voice);
assert!(channel.is_voice());
assert!(!channel.is_text());
channel.channel_type = Some(ChannelType::Group);
assert!(channel.is_group());
}
#[test]
fn test_channel_type_conversion() {
assert_eq!(ChannelType::from(0), ChannelType::Text);
assert_eq!(u32::from(ChannelType::Text), 0);
assert_eq!(ChannelType::from(10005), ChannelType::Live);
assert_eq!(u32::from(ChannelType::Live), 10005);
assert_eq!(ChannelType::from(99999), ChannelType::Unknown(99999));
assert_eq!(u32::from(ChannelType::Unknown(99999)), 99999);
}
#[test]
fn test_private_types() {
let mut channel = Channel::new();
channel.private_type = Some(PrivateType::Public);
assert!(channel.is_public());
assert!(!channel.is_admin_only());
channel.private_type = Some(PrivateType::AdminOnly);
assert!(!channel.is_public());
assert!(channel.is_admin_only());
channel.private_type = Some(PrivateType::AdminAndSpecifiedMembers);
assert!(channel.is_specified_users_only());
}
#[test]
fn test_speak_permissions() {
let mut channel = Channel::new();
channel.speak_permission = Some(SpeakPermission::Everyone);
assert!(channel.everyone_can_speak());
assert!(!channel.admin_only_speak());
channel.speak_permission = Some(SpeakPermission::AdminAndSpecifiedMembers);
assert!(!channel.everyone_can_speak());
assert!(channel.admin_only_speak());
}
#[test]
fn test_channel_mention() {
let mut channel = Channel::new();
channel.id = Some("123456789".to_string());
assert_eq!(channel.mention(), "<#123456789>");
}
#[test]
fn test_channel_permissions() {
let mut perms = ChannelPermissions::new();
assert!(!perms.is_user_permission());
assert!(!perms.is_role_permission());
perms.user_id = Some("user123".to_string());
assert!(perms.is_user_permission());
assert!(!perms.is_role_permission());
perms.user_id = None;
perms.role_id = Some("role123".to_string());
assert!(!perms.is_user_permission());
assert!(perms.is_role_permission());
}
}