pub mod audit_log;
pub mod automod;
mod emoji;
mod guild_id;
mod guild_preview;
mod integration;
mod member;
mod partial_guild;
mod premium_tier;
mod role;
mod scheduled_event;
mod system_channel;
mod welcome_screen;
#[cfg(feature = "model")]
use futures::stream::StreamExt;
use serde::de::Error as DeError;
#[cfg(feature = "model")]
use tracing::error;
#[cfg(all(feature = "model", feature = "cache"))]
use tracing::warn;
#[doc(hidden)]
#[deprecated(note = "import the types from the `audit_log` module")]
pub use self::audit_log::*;
pub use self::emoji::*;
pub use self::guild_id::*;
pub use self::guild_preview::*;
pub use self::integration::*;
pub use self::member::*;
pub use self::partial_guild::*;
pub use self::premium_tier::*;
pub use self::role::*;
pub use self::scheduled_event::*;
pub use self::system_channel::*;
pub use self::welcome_screen::*;
use super::utils::*;
#[cfg(feature = "model")]
use crate::builder::{
AddMember,
CreateApplicationCommand,
CreateApplicationCommandPermissionsData,
CreateApplicationCommands,
CreateChannel,
CreateScheduledEvent,
CreateSticker,
EditAutoModRule,
EditGuild,
EditGuildWelcomeScreen,
EditGuildWidget,
EditMember,
EditRole,
EditScheduledEvent,
EditSticker,
};
#[cfg(all(feature = "cache", feature = "model"))]
use crate::cache::Cache;
#[cfg(feature = "collector")]
use crate::client::bridge::gateway::ShardMessenger;
#[cfg(feature = "collector")]
use crate::collector::{
CollectReaction,
CollectReply,
MessageCollectorBuilder,
ReactionCollectorBuilder,
};
#[cfg(feature = "model")]
use crate::constants::LARGE_THRESHOLD;
#[cfg(feature = "model")]
use crate::http::{CacheHttp, Http, UserPagination};
use crate::json::prelude::*;
use crate::json::{from_number, from_value};
#[cfg(feature = "model")]
use crate::model::application::command::{Command, CommandPermission};
#[cfg(feature = "model")]
use crate::model::guild::automod::Rule;
use crate::model::prelude::*;
use crate::model::utils::{emojis, presences, roles, stickers};
use crate::model::Timestamp;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
pub struct Ban {
pub reason: Option<String>,
pub user: User,
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct Guild {
pub afk_channel_id: Option<ChannelId>,
pub afk_timeout: u64,
pub application_id: Option<ApplicationId>,
#[serde(serialize_with = "serialize_map_values")]
pub channels: HashMap<ChannelId, Channel>,
pub default_message_notifications: DefaultMessageNotificationLevel,
#[serde(serialize_with = "serialize_map_values")]
pub emojis: HashMap<EmojiId, Emoji>,
pub explicit_content_filter: ExplicitContentFilter,
pub features: Vec<String>,
pub icon: Option<String>,
pub id: GuildId,
pub joined_at: Timestamp,
pub large: bool,
pub member_count: u64,
#[serde(serialize_with = "serialize_map_values")]
pub members: HashMap<UserId, Member>,
pub mfa_level: MfaLevel,
pub name: String,
pub owner_id: UserId,
#[serde(serialize_with = "serialize_map_values")]
pub presences: HashMap<UserId, Presence>,
#[serde(serialize_with = "serialize_map_values")]
pub roles: HashMap<RoleId, Role>,
pub splash: Option<String>,
pub discovery_splash: Option<String>,
pub system_channel_id: Option<ChannelId>,
pub system_channel_flags: SystemChannelFlags,
pub rules_channel_id: Option<ChannelId>,
pub public_updates_channel_id: Option<ChannelId>,
pub verification_level: VerificationLevel,
#[serde(serialize_with = "serialize_map_values")]
pub voice_states: HashMap<UserId, VoiceState>,
pub description: Option<String>,
#[serde(default)]
pub premium_tier: PremiumTier,
#[serde(default)]
pub premium_subscription_count: u64,
pub banner: Option<String>,
pub vanity_url_code: Option<String>,
pub preferred_locale: String,
pub welcome_screen: Option<GuildWelcomeScreen>,
pub approximate_member_count: Option<u64>,
pub approximate_presence_count: Option<u64>,
pub nsfw_level: NsfwLevel,
pub max_video_channel_users: Option<u64>,
pub max_presences: Option<u64>,
pub max_members: Option<u64>,
pub widget_enabled: Option<bool>,
pub widget_channel_id: Option<ChannelId>,
#[serde(default)]
pub stage_instances: Vec<StageInstance>,
#[serde(default)]
pub threads: Vec<GuildChannel>,
#[serde(serialize_with = "serialize_map_values")]
pub stickers: HashMap<StickerId, Sticker>,
}
#[cfg(feature = "model")]
impl Guild {
#[inline]
pub async fn automod_rules(self, http: impl AsRef<Http>) -> Result<Vec<Rule>> {
self.id.automod_rules(http).await
}
#[inline]
pub async fn automod_rule(
self,
http: impl AsRef<Http>,
rule_id: impl Into<RuleId>,
) -> Result<Rule> {
self.id.automod_rule(http, rule_id).await
}
#[inline]
pub async fn create_automod_rule(
self,
http: impl AsRef<Http>,
f: impl FnOnce(&mut EditAutoModRule) -> &mut EditAutoModRule,
) -> Result<Rule> {
self.id.create_automod_rule(http, f).await
}
#[inline]
pub async fn edit_automod_rule(
self,
http: impl AsRef<Http>,
rule_id: impl Into<RuleId>,
f: impl FnOnce(&mut EditAutoModRule) -> &mut EditAutoModRule,
) -> Result<Rule> {
self.id.edit_automod_rule(http, rule_id, f).await
}
#[inline]
pub async fn delete_automod_rule(
self,
http: impl AsRef<Http>,
rule_id: impl Into<RuleId>,
) -> Result<()> {
self.id.delete_automod_rule(http, rule_id).await
}
#[cfg(feature = "cache")]
fn check_hierarchy(&self, cache: impl AsRef<Cache>, other_user: UserId) -> Result<()> {
let current_id = cache.as_ref().current_user().id;
if let Some(higher) = self.greater_member_hierarchy(&cache, other_user, current_id) {
if higher != current_id {
return Err(Error::Model(ModelError::Hierarchy));
}
}
Ok(())
}
#[allow(clippy::unused_async)]
pub async fn default_channel(&self, uid: UserId) -> Option<&GuildChannel> {
let member = self.members.get(&uid)?;
for channel in self.channels.values() {
if let Channel::Guild(channel) = channel {
if self.user_permissions_in(channel, member).ok()?.view_channel() {
return Some(channel);
}
}
}
None
}
#[must_use]
pub fn default_channel_guaranteed(&self) -> Option<&GuildChannel> {
for channel in self.channels.values() {
if let Channel::Guild(channel) = channel {
if self
.members
.values()
.filter_map(|member| self.user_permissions_in(channel, member).ok())
.all(Permissions::view_channel)
{
return Some(channel);
}
}
}
None
}
#[cfg(feature = "cache")]
async fn has_perms(&self, cache_http: impl CacheHttp, mut permissions: Permissions) -> bool {
if let Some(cache) = cache_http.cache() {
let user_id = cache.current_user().id;
if let Ok(perms) = self.member_permissions(&cache_http, user_id).await {
permissions.remove(perms);
permissions.is_empty()
} else {
false
}
} else {
false
}
}
#[cfg(feature = "cache")]
pub fn channel_id_from_name(
&self,
cache: impl AsRef<Cache>,
name: impl AsRef<str>,
) -> Option<ChannelId> {
let name = name.as_ref();
let guild_channels = cache.as_ref().guild_channels(self.id)?;
for channel_entry in guild_channels.iter() {
let (id, channel) = channel_entry.pair();
if channel.name == name {
return Some(*id);
}
}
None
}
#[inline]
pub async fn ban(
&self,
cache_http: impl CacheHttp,
user: impl Into<UserId>,
dmd: u8,
) -> Result<()> {
self._ban_with_reason(cache_http, user.into(), dmd, "").await
}
#[inline]
pub async fn ban_with_reason(
&self,
cache_http: impl CacheHttp,
user: impl Into<UserId>,
dmd: u8,
reason: impl AsRef<str>,
) -> Result<()> {
self._ban_with_reason(cache_http, user.into(), dmd, reason.as_ref()).await
}
async fn _ban_with_reason(
&self,
cache_http: impl CacheHttp,
user: UserId,
dmd: u8,
reason: &str,
) -> Result<()> {
#[cfg(feature = "cache")]
{
if let Some(cache) = cache_http.cache() {
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
self.check_hierarchy(cache, user)?;
}
}
self.id.ban_with_reason(cache_http.http(), user, dmd, reason).await
}
#[must_use]
pub fn banner_url(&self) -> Option<String> {
self.banner.as_ref().map(|banner| cdn!("/banners/{}/{}.webp?size=1024", self.id, banner))
}
pub async fn bans(&self, cache_http: impl CacheHttp) -> Result<Vec<Ban>> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.bans(cache_http.http()).await
}
#[inline]
pub async fn add_member(
&self,
http: impl AsRef<Http>,
user_id: impl Into<UserId>,
f: impl FnOnce(&mut AddMember) -> &mut AddMember,
) -> Result<Option<Member>> {
self.id.add_member(http, user_id, f).await
}
#[inline]
pub async fn audit_logs(
&self,
http: impl AsRef<Http>,
action_type: Option<u8>,
user_id: Option<UserId>,
before: Option<AuditLogEntryId>,
limit: Option<u8>,
) -> Result<AuditLogs> {
self.id.audit_logs(&http, action_type, user_id, before, limit).await
}
#[inline]
pub async fn channels(
&self,
http: impl AsRef<Http>,
) -> Result<HashMap<ChannelId, GuildChannel>> {
self.id.channels(&http).await
}
pub async fn create(
http: impl AsRef<Http>,
name: &str,
icon: Option<&str>,
) -> Result<PartialGuild> {
let map = json!({
"icon": icon,
"name": name,
});
http.as_ref().create_guild(&map).await
}
pub async fn create_channel(
&self,
cache_http: impl CacheHttp,
f: impl FnOnce(&mut CreateChannel) -> &mut CreateChannel,
) -> Result<GuildChannel> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_CHANNELS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.create_channel(cache_http.http(), f).await
}
#[inline]
pub async fn create_emoji(
&self,
http: impl AsRef<Http>,
name: &str,
image: &str,
) -> Result<Emoji> {
self.id.create_emoji(&http, name, image).await
}
#[inline]
pub async fn create_integration(
&self,
http: impl AsRef<Http>,
integration_id: impl Into<IntegrationId>,
kind: &str,
) -> Result<()> {
self.id.create_integration(&http, integration_id, kind).await
}
#[allow(clippy::missing_errors_doc)]
#[inline]
pub async fn create_application_command<F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<Command>
where
F: FnOnce(&mut CreateApplicationCommand) -> &mut CreateApplicationCommand,
{
self.id.create_application_command(http, f).await
}
pub async fn set_application_commands<F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<Vec<Command>>
where
F: FnOnce(&mut CreateApplicationCommands) -> &mut CreateApplicationCommands,
{
self.id.set_application_commands(http, f).await
}
pub async fn create_application_command_permission<F>(
&self,
http: impl AsRef<Http>,
command_id: CommandId,
f: F,
) -> Result<CommandPermission>
where
F: FnOnce(
&mut CreateApplicationCommandPermissionsData,
) -> &mut CreateApplicationCommandPermissionsData,
{
self.id.create_application_command_permission(http, command_id, f).await
}
#[deprecated(note = "use `create_appliction_command_permission`.")]
#[allow(deprecated)]
pub async fn set_application_commands_permissions<F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<Vec<CommandPermission>>
where
F: FnOnce(
&mut crate::builder::CreateApplicationCommandsPermissions,
) -> &mut crate::builder::CreateApplicationCommandsPermissions,
{
self.id.set_application_commands_permissions(http, f).await
}
pub async fn get_application_commands(&self, http: impl AsRef<Http>) -> Result<Vec<Command>> {
self.id.get_application_commands(http).await
}
pub async fn get_application_commands_with_localizations(
&self,
http: impl AsRef<Http>,
) -> Result<Vec<Command>> {
self.id.get_application_commands_with_localizations(http).await
}
pub async fn get_application_command(
&self,
http: impl AsRef<Http>,
command_id: CommandId,
) -> Result<Command> {
self.id.get_application_command(http, command_id).await
}
pub async fn edit_application_command<F>(
&self,
http: impl AsRef<Http>,
command_id: CommandId,
f: F,
) -> Result<Command>
where
F: FnOnce(&mut CreateApplicationCommand) -> &mut CreateApplicationCommand,
{
self.id.edit_application_command(http, command_id, f).await
}
pub async fn delete_application_command(
&self,
http: impl AsRef<Http>,
command_id: CommandId,
) -> Result<()> {
self.id.delete_application_command(http, command_id).await
}
pub async fn get_application_commands_permissions(
&self,
http: impl AsRef<Http>,
) -> Result<Vec<CommandPermission>> {
self.id.get_application_commands_permissions(http).await
}
pub async fn get_application_command_permissions(
&self,
http: impl AsRef<Http>,
command_id: CommandId,
) -> Result<CommandPermission> {
self.id.get_application_command_permissions(http, command_id).await
}
pub async fn create_role<F>(&self, cache_http: impl CacheHttp, f: F) -> Result<Role>
where
F: FnOnce(&mut EditRole) -> &mut EditRole,
{
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_ROLES;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.create_role(cache_http.http(), f).await
}
pub async fn create_scheduled_event<F>(
&self,
cache_http: impl CacheHttp,
f: F,
) -> Result<ScheduledEvent>
where
F: FnOnce(&mut CreateScheduledEvent) -> &mut CreateScheduledEvent,
{
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_EVENTS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.create_scheduled_event(cache_http.http(), f).await
}
pub async fn create_sticker<'a, F>(&self, cache_http: impl CacheHttp, f: F) -> Result<Sticker>
where
for<'b> F: FnOnce(&'b mut CreateSticker<'a>) -> &'b mut CreateSticker<'a>,
{
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_EMOJIS_AND_STICKERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.create_sticker(cache_http.http(), f).await
}
pub async fn delete(&self, cache_http: impl CacheHttp) -> Result<PartialGuild> {
#[cfg(feature = "cache")]
{
if let Some(cache) = cache_http.cache() {
if self.owner_id != cache.current_user().id {
let req = Permissions::MANAGE_GUILD;
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.delete(cache_http.http()).await
}
#[inline]
pub async fn delete_emoji(
&self,
http: impl AsRef<Http>,
emoji_id: impl Into<EmojiId>,
) -> Result<()> {
self.id.delete_emoji(&http, emoji_id).await
}
#[inline]
pub async fn delete_integration(
&self,
http: impl AsRef<Http>,
integration_id: impl Into<IntegrationId>,
) -> Result<()> {
self.id.delete_integration(&http, integration_id).await
}
#[inline]
pub async fn delete_role(
&self,
http: impl AsRef<Http>,
role_id: impl Into<RoleId>,
) -> Result<()> {
self.id.delete_role(&http, role_id).await
}
#[inline]
pub async fn delete_scheduled_event(
&self,
http: impl AsRef<Http>,
event_id: impl Into<ScheduledEventId>,
) -> Result<()> {
self.id.delete_scheduled_event(&http, event_id).await
}
#[inline]
pub async fn delete_sticker(
&self,
http: impl AsRef<Http>,
sticker_id: impl Into<StickerId>,
) -> Result<()> {
self.id.delete_sticker(&http, sticker_id).await
}
pub async fn edit<F>(&mut self, cache_http: impl CacheHttp, f: F) -> Result<()>
where
F: FnOnce(&mut EditGuild) -> &mut EditGuild,
{
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_GUILD;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
match self.id.edit(cache_http.http(), f).await {
Ok(guild) => {
self.afk_channel_id = guild.afk_channel_id;
self.afk_timeout = guild.afk_timeout;
self.default_message_notifications = guild.default_message_notifications;
self.emojis = guild.emojis;
self.features = guild.features;
self.icon = guild.icon;
self.mfa_level = guild.mfa_level;
self.name = guild.name;
self.owner_id = guild.owner_id;
self.roles = guild.roles;
self.splash = guild.splash;
self.verification_level = guild.verification_level;
Ok(())
},
Err(why) => Err(why),
}
}
#[inline]
pub async fn edit_emoji(
&self,
http: impl AsRef<Http>,
emoji_id: impl Into<EmojiId>,
name: &str,
) -> Result<Emoji> {
self.id.edit_emoji(&http, emoji_id, name).await
}
#[inline]
pub async fn edit_member<F>(
&self,
http: impl AsRef<Http>,
user_id: impl Into<UserId>,
f: F,
) -> Result<Member>
where
F: FnOnce(&mut EditMember) -> &mut EditMember,
{
self.id.edit_member(&http, user_id, f).await
}
pub async fn edit_nickname(
&self,
cache_http: impl CacheHttp,
new_nickname: Option<&str>,
) -> Result<()> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::CHANGE_NICKNAME;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.edit_nickname(cache_http.http(), new_nickname).await
}
#[inline]
pub async fn edit_role<F>(
&self,
http: impl AsRef<Http>,
role_id: impl Into<RoleId>,
f: F,
) -> Result<Role>
where
F: FnOnce(&mut EditRole) -> &mut EditRole,
{
self.id.edit_role(&http, role_id, f).await
}
#[inline]
pub async fn edit_role_position(
&self,
http: impl AsRef<Http>,
role_id: impl Into<RoleId>,
position: u64,
) -> Result<Vec<Role>> {
self.id.edit_role_position(&http, role_id, position).await
}
pub async fn edit_scheduled_event<F>(
&self,
http: impl AsRef<Http>,
event_id: impl Into<ScheduledEventId>,
f: F,
) -> Result<ScheduledEvent>
where
F: FnOnce(&mut EditScheduledEvent) -> &mut EditScheduledEvent,
{
self.id.edit_scheduled_event(&http, event_id, f).await
}
#[inline]
pub async fn edit_sticker<F>(
&self,
http: impl AsRef<Http>,
sticker_id: impl Into<StickerId>,
f: F,
) -> Result<Sticker>
where
F: FnOnce(&mut EditSticker) -> &mut EditSticker,
{
self.id.edit_sticker(&http, sticker_id, f).await
}
pub async fn edit_welcome_screen<F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<GuildWelcomeScreen>
where
F: FnOnce(&mut EditGuildWelcomeScreen) -> &mut EditGuildWelcomeScreen,
{
self.id.edit_welcome_screen(http, f).await
}
pub async fn edit_widget<F>(&self, http: impl AsRef<Http>, f: F) -> Result<GuildWidget>
where
F: FnOnce(&mut EditGuildWidget) -> &mut EditGuildWidget,
{
self.id.edit_widget(http, f).await
}
#[inline]
pub async fn get(http: impl AsRef<Http>, guild_id: impl Into<GuildId>) -> Result<PartialGuild> {
guild_id.into().to_partial_guild(&http).await
}
#[cfg(feature = "cache")]
#[inline]
pub fn greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
lhs_id: impl Into<UserId>,
rhs_id: impl Into<UserId>,
) -> Option<UserId> {
self._greater_member_hierarchy(&cache, lhs_id.into(), rhs_id.into())
}
#[cfg(feature = "cache")]
fn _greater_member_hierarchy(
&self,
cache: impl AsRef<Cache>,
lhs_id: UserId,
rhs_id: UserId,
) -> Option<UserId> {
if lhs_id == rhs_id {
return None;
}
if lhs_id == self.owner_id {
return Some(lhs_id);
} else if rhs_id == self.owner_id {
return Some(rhs_id);
}
let lhs = self.members.get(&lhs_id)?.highest_role_info(&cache).unwrap_or((RoleId(0), 0));
let rhs = self.members.get(&rhs_id)?.highest_role_info(&cache).unwrap_or((RoleId(0), 0));
if (lhs.1 == 0 && rhs.1 == 0) || (lhs.0 == rhs.0) {
return None;
}
if lhs.1 > rhs.1 {
return Some(lhs_id);
}
if rhs.1 > lhs.1 {
return Some(rhs_id);
}
if lhs.1 == rhs.1 && lhs.0 < rhs.0 {
Some(lhs_id)
} else {
Some(rhs_id)
}
}
#[must_use]
pub fn icon_url(&self) -> Option<String> {
self.icon.as_ref().map(|icon| {
let ext = if icon.starts_with("a_") { "gif" } else { "webp" };
cdn!("/icons/{}/{}.{}", self.id, icon, ext)
})
}
#[inline]
pub async fn emojis(&self, http: impl AsRef<Http>) -> Result<Vec<Emoji>> {
self.id.emojis(http).await
}
#[inline]
pub async fn emoji(&self, http: impl AsRef<Http>, emoji_id: EmojiId) -> Result<Emoji> {
self.id.emoji(http, emoji_id).await
}
#[inline]
pub async fn integrations(&self, http: impl AsRef<Http>) -> Result<Vec<Integration>> {
self.id.integrations(&http).await
}
pub async fn invites(&self, cache_http: impl CacheHttp) -> Result<Vec<RichInvite>> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::MANAGE_GUILD;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.invites(cache_http.http()).await
}
#[inline]
#[must_use]
pub fn is_large(&self) -> bool {
self.members.len() > LARGE_THRESHOLD as usize
}
#[inline]
pub async fn kick(&self, http: impl AsRef<Http>, user_id: impl Into<UserId>) -> Result<()> {
self.id.kick(&http, user_id).await
}
#[inline]
pub async fn kick_with_reason(
&self,
http: impl AsRef<Http>,
user_id: impl Into<UserId>,
reason: &str,
) -> Result<()> {
self.id.kick_with_reason(&http, user_id, reason).await
}
#[inline]
pub async fn leave(&self, http: impl AsRef<Http>) -> Result<()> {
self.id.leave(&http).await
}
#[inline]
pub async fn member(
&self,
cache_http: impl CacheHttp,
user_id: impl Into<UserId>,
) -> Result<Member> {
self.id.member(cache_http, user_id).await
}
#[inline]
pub async fn members(
&self,
http: impl AsRef<Http>,
limit: Option<u64>,
after: impl Into<Option<UserId>>,
) -> Result<Vec<Member>> {
self.id.members(&http, limit, after).await
}
#[must_use]
pub fn members_with_status(&self, status: OnlineStatus) -> Vec<&Member> {
let mut members = vec![];
for (&id, member) in &self.members {
if let Some(presence) = self.presences.get(&id) {
if status == presence.status {
members.push(member);
}
}
}
members
}
#[must_use]
pub fn member_named(&self, name: &str) -> Option<&Member> {
let (username, discrim) = match crate::utils::parse_user_tag(name) {
Some((username, discrim)) => (username, Some(discrim)),
None => (name, None),
};
for member in self.members.values() {
if member.user.name == username
&& discrim.map_or(true, |d| member.user.discriminator == d)
{
return Some(member);
}
}
self.members.values().find(|member| member.nick.as_ref().map_or(false, |nick| nick == name))
}
pub async fn members_starting_with(
&self,
prefix: &str,
case_sensitive: bool,
sorted: bool,
) -> Vec<(&Member, String)> {
fn starts_with(prefix: &str, case_sensitive: bool, name: &str) -> bool {
case_sensitive && name.starts_with(prefix)
|| !case_sensitive && starts_with_case_insensitive(name, prefix)
}
let mut members = futures::stream::iter(self.members.values())
.filter_map(|member| async move {
let username = &member.user.name;
if starts_with(prefix, case_sensitive, username) {
Some((member, username.to_string()))
} else {
match member.nick {
Some(ref nick) => {
if starts_with(prefix, case_sensitive, nick) {
Some((member, nick.to_string()))
} else {
None
}
},
None => None,
}
}
})
.collect::<Vec<(&Member, String)>>()
.await;
if sorted {
members.sort_by(|a, b| closest_to_origin(prefix, &a.1[..], &b.1[..]));
}
members
}
pub async fn members_containing(
&self,
substring: &str,
case_sensitive: bool,
sorted: bool,
) -> Vec<(&Member, String)> {
fn contains(substring: &str, case_sensitive: bool, name: &str) -> bool {
case_sensitive && name.contains(substring)
|| !case_sensitive && contains_case_insensitive(name, substring)
}
let mut members = futures::stream::iter(self.members.values())
.filter_map(|member| async move {
let username = &member.user.name;
if contains(substring, case_sensitive, username) {
Some((member, username.to_string()))
} else {
match member.nick {
Some(ref nick) => {
if contains(substring, case_sensitive, nick) {
Some((member, nick.to_string()))
} else {
None
}
},
None => None,
}
}
})
.collect::<Vec<(&Member, String)>>()
.await;
if sorted {
members.sort_by(|a, b| closest_to_origin(substring, &a.1[..], &b.1[..]));
}
members
}
pub async fn members_username_containing(
&self,
substring: &str,
case_sensitive: bool,
sorted: bool,
) -> Vec<(&Member, String)> {
let mut members = futures::stream::iter(self.members.values())
.filter_map(|member| async move {
let name = &member.user.name;
if (case_sensitive && name.contains(substring))
|| contains_case_insensitive(name, substring)
{
Some((member, name.to_string()))
} else {
None
}
})
.collect::<Vec<(&Member, String)>>()
.await;
if sorted {
members.sort_by(|a, b| closest_to_origin(substring, &a.1[..], &b.1[..]));
}
members
}
pub async fn members_nick_containing(
&self,
substring: &str,
case_sensitive: bool,
sorted: bool,
) -> Vec<(&Member, String)> {
let mut members = futures::stream::iter(self.members.values())
.filter_map(|member| async move {
let nick = match member.nick {
Some(ref nick) => nick.to_string(),
None => member.user.name.to_string(),
};
if case_sensitive && nick.contains(substring)
|| !case_sensitive && contains_case_insensitive(&nick, substring)
{
Some((member, nick))
} else {
None
}
})
.collect::<Vec<(&Member, String)>>()
.await;
if sorted {
members.sort_by(|a, b| closest_to_origin(substring, &a.1[..], &b.1[..]));
}
members
}
#[inline]
#[cfg(feature = "cache")]
pub async fn member_permissions(
&self,
cache_http: impl CacheHttp,
user_id: impl Into<UserId>,
) -> Result<Permissions> {
self._member_permissions(cache_http, user_id.into()).await
}
#[cfg(feature = "cache")]
async fn _member_permissions(
&self,
cache_http: impl CacheHttp,
user_id: UserId,
) -> Result<Permissions> {
if user_id == self.owner_id {
return Ok(Permissions::all());
}
let member = self.member(cache_http, &user_id).await?;
Ok(self._member_permission_from_member(&member))
}
#[cfg(feature = "cache")]
pub(crate) fn _member_permission_from_member(&self, member: &Member) -> Permissions {
if member.user.id == self.owner_id {
return Permissions::all();
}
let everyone = if let Some(everyone) = self.roles.get(&RoleId(self.id.0)) {
everyone
} else {
error!("@everyone role ({}) missing in '{}'", self.id, self.name);
return Permissions::empty();
};
let mut permissions = everyone.permissions;
for role in &member.roles {
if let Some(role) = self.roles.get(role) {
if role.permissions.contains(Permissions::ADMINISTRATOR) {
return Permissions::all();
}
permissions |= role.permissions;
} else {
warn!("{} on {} has non-existent role {:?}", member.user.id, self.id, role,);
}
}
permissions
}
#[inline]
pub async fn move_member(
&self,
http: impl AsRef<Http>,
user_id: impl Into<UserId>,
channel_id: impl Into<ChannelId>,
) -> Result<Member> {
self.id.move_member(&http, user_id, channel_id).await
}
#[inline]
pub fn user_permissions_in(
&self,
channel: &GuildChannel,
member: &Member,
) -> Result<Permissions> {
Self::_user_permissions_in(channel, member, &self.roles, self.owner_id, self.id)
}
pub(crate) fn _user_permissions_in(
channel: &GuildChannel,
member: &Member,
roles: &HashMap<RoleId, Role>,
owner_id: UserId,
guild_id: GuildId,
) -> Result<Permissions> {
if member.user.id == owner_id {
return Ok(Self::remove_unnecessary_voice_permissions(channel, Permissions::all()));
}
let everyone = if let Some(everyone) = roles.get(&RoleId(guild_id.0)) {
everyone
} else {
error!("@everyone role missing in {}", guild_id,);
return Err(Error::Model(ModelError::RoleNotFound));
};
let mut permissions = everyone.permissions;
for &role in &member.roles {
if let Some(role) = roles.get(&role) {
permissions |= role.permissions;
} else {
error!("{} on {} has non-existent role {:?}", member.user.id, guild_id, role);
return Err(Error::Model(ModelError::RoleNotFound));
}
}
if permissions.contains(Permissions::ADMINISTRATOR) {
return Ok(Self::remove_unnecessary_voice_permissions(channel, Permissions::all()));
}
let mut data = Vec::with_capacity(member.roles.len());
for overwrite in &channel.permission_overwrites {
if let PermissionOverwriteType::Role(role) = overwrite.kind {
if role.0 != guild_id.0 && !member.roles.contains(&role) {
continue;
}
if let Some(role) = roles.get(&role) {
data.push((role.position, overwrite.deny, overwrite.allow));
}
}
}
data.sort_by(|a, b| a.0.cmp(&b.0));
for overwrite in data {
permissions = (permissions & !overwrite.1) | overwrite.2;
}
for overwrite in &channel.permission_overwrites {
if PermissionOverwriteType::Member(member.user.id) != overwrite.kind {
continue;
}
permissions = (permissions & !overwrite.deny) | overwrite.allow;
}
if channel.id.0 == guild_id.0 {
permissions |= Permissions::VIEW_CHANNEL;
}
Self::remove_unusable_permissions(&mut permissions);
Ok(permissions)
}
#[inline]
pub fn role_permissions_in(&self, channel: &GuildChannel, role: &Role) -> Result<Permissions> {
Self::_role_permissions_in(channel, role, self.id)
}
pub(crate) fn _role_permissions_in(
channel: &GuildChannel,
role: &Role,
guild_id: GuildId,
) -> Result<Permissions> {
if role.guild_id != guild_id || channel.guild_id != guild_id {
return Err(Error::Model(ModelError::WrongGuild));
}
let mut permissions = role.permissions;
if permissions.contains(Permissions::ADMINISTRATOR) {
return Ok(Self::remove_unnecessary_voice_permissions(channel, Permissions::all()));
}
for overwrite in &channel.permission_overwrites {
if let PermissionOverwriteType::Role(permissions_role_id) = overwrite.kind {
if permissions_role_id == role.id {
permissions = (permissions & !overwrite.deny) | overwrite.allow;
break;
}
}
}
Self::remove_unusable_permissions(&mut permissions);
Ok(permissions)
}
pub async fn prune_count(&self, cache_http: impl CacheHttp, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::KICK_MEMBERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.prune_count(cache_http.http(), days).await
}
pub(crate) fn remove_unusable_permissions(permissions: &mut Permissions) {
if !permissions.contains(Permissions::SEND_MESSAGES) {
*permissions &= !(Permissions::SEND_TTS_MESSAGES
| Permissions::MENTION_EVERYONE
| Permissions::EMBED_LINKS
| Permissions::ATTACH_FILES);
}
if !permissions.contains(Permissions::VIEW_CHANNEL) {
*permissions &= !(Permissions::KICK_MEMBERS
| Permissions::BAN_MEMBERS
| Permissions::ADMINISTRATOR
| Permissions::MANAGE_GUILD
| Permissions::CHANGE_NICKNAME
| Permissions::MANAGE_NICKNAMES);
}
}
pub(crate) fn remove_unnecessary_voice_permissions(
channel: &GuildChannel,
mut permissions: Permissions,
) -> Permissions {
if channel.kind == ChannelType::Text {
permissions &= !(Permissions::CONNECT
| Permissions::SPEAK
| Permissions::MUTE_MEMBERS
| Permissions::DEAFEN_MEMBERS
| Permissions::MOVE_MEMBERS
| Permissions::USE_VAD
| Permissions::STREAM);
}
permissions
}
#[inline]
pub async fn reorder_channels<It>(&self, http: impl AsRef<Http>, channels: It) -> Result<()>
where
It: IntoIterator<Item = (ChannelId, u64)>,
{
self.id.reorder_channels(&http, channels).await
}
#[inline]
pub async fn search_members(
&self,
http: impl AsRef<Http>,
query: &str,
limit: Option<u64>,
) -> Result<Vec<Member>> {
self.id.search_members(http, query, limit).await
}
pub async fn scheduled_event(
self,
http: impl AsRef<Http>,
event_id: impl Into<ScheduledEventId>,
with_user_count: bool,
) -> Result<ScheduledEvent> {
self.id.scheduled_event(&http, event_id, with_user_count).await
}
pub async fn scheduled_events(
self,
http: impl AsRef<Http>,
with_user_count: bool,
) -> Result<Vec<ScheduledEvent>> {
self.id.scheduled_events(&http, with_user_count).await
}
pub async fn scheduled_event_users(
self,
http: impl AsRef<Http>,
event_id: impl Into<ScheduledEventId>,
limit: Option<u64>,
) -> Result<Vec<ScheduledEventUser>> {
self.id.scheduled_event_users(&http, event_id, limit).await
}
pub async fn scheduled_event_users_optioned(
self,
http: impl AsRef<Http>,
event_id: impl Into<ScheduledEventId>,
limit: Option<u64>,
target: Option<UserPagination>,
with_member: Option<bool>,
) -> Result<Vec<ScheduledEventUser>> {
self.id.scheduled_event_users_optioned(&http, event_id, limit, target, with_member).await
}
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
pub fn shard_id(&self, cache: impl AsRef<Cache>) -> u64 {
self.id.shard_id(&cache)
}
#[cfg(all(feature = "utils", not(feature = "cache")))]
#[inline]
#[must_use]
pub fn shard_id(&self, shard_count: u64) -> u64 {
self.id.shard_id(shard_count)
}
#[must_use]
pub fn splash_url(&self) -> Option<String> {
self.splash.as_ref().map(|splash| cdn!("/splashes/{}/{}.webp?size=4096", self.id, splash))
}
#[inline]
pub async fn start_integration_sync(
&self,
http: impl AsRef<Http>,
integration_id: impl Into<IntegrationId>,
) -> Result<()> {
self.id.start_integration_sync(&http, integration_id).await
}
pub async fn start_prune(&self, cache_http: impl CacheHttp, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::KICK_MEMBERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.start_prune(cache_http.http(), days).await
}
pub async fn unban(
&self,
cache_http: impl CacheHttp,
user_id: impl Into<UserId>,
) -> Result<()> {
#[cfg(feature = "cache")]
{
if cache_http.cache().is_some() {
let req = Permissions::BAN_MEMBERS;
if !self.has_perms(&cache_http, req).await {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
}
self.id.unban(&cache_http.http(), user_id).await
}
#[inline]
pub async fn vanity_url(&self, http: impl AsRef<Http>) -> Result<String> {
self.id.vanity_url(&http).await
}
#[inline]
pub async fn webhooks(&self, http: impl AsRef<Http>) -> Result<Vec<Webhook>> {
self.id.webhooks(&http).await
}
#[must_use]
pub fn role_by_name(&self, role_name: &str) -> Option<&Role> {
self.roles.values().find(|role| role_name == role.name)
}
#[cfg(feature = "collector")]
pub fn await_reply(&self, shard_messenger: impl AsRef<ShardMessenger>) -> CollectReply {
CollectReply::new(shard_messenger).guild_id(self.id.0)
}
#[cfg(feature = "collector")]
pub fn await_replies(
&self,
shard_messenger: impl AsRef<ShardMessenger>,
) -> MessageCollectorBuilder {
MessageCollectorBuilder::new(shard_messenger).guild_id(self.id.0)
}
#[cfg(feature = "collector")]
pub fn await_reaction(&self, shard_messenger: impl AsRef<ShardMessenger>) -> CollectReaction {
CollectReaction::new(shard_messenger).guild_id(self.id.0)
}
#[cfg(feature = "collector")]
pub fn await_reactions(
&self,
shard_messenger: impl AsRef<ShardMessenger>,
) -> ReactionCollectorBuilder {
ReactionCollectorBuilder::new(shard_messenger).guild_id(self.id.0)
}
pub async fn get_active_threads(&self, http: impl AsRef<Http>) -> Result<ThreadsData> {
self.id.get_active_threads(http).await
}
}
impl<'de> Deserialize<'de> for Guild {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
let id = map.get("id").and_then(Value::as_str).and_then(|x| x.parse::<u64>().ok());
if let Some(guild_id) = id {
if let Some(array) = map.get_mut("channels").and_then(Value::as_array_mut) {
for value in array {
if let Some(channel) = value.as_object_mut() {
channel.insert("guild_id".to_string(), from_number(guild_id));
}
}
}
if let Some(array) = map.get_mut("members").and_then(Value::as_array_mut) {
for value in array {
if let Some(member) = value.as_object_mut() {
member.insert("guild_id".to_string(), from_number(guild_id));
}
}
}
if let Some(array) = map.get_mut("roles").and_then(Value::as_array_mut) {
for value in array {
if let Some(role) = value.as_object_mut() {
role.insert("guild_id".to_string(), from_number(guild_id));
}
}
}
}
let afk_channel_id = match map.remove("afk_channel_id") {
Some(v) => from_value::<Option<ChannelId>>(v).map_err(DeError::custom)?,
None => None,
};
let afk_timeout = map
.remove("afk_timeout")
.ok_or_else(|| DeError::custom("expected guild afk_timeout"))
.and_then(u64::deserialize)
.map_err(DeError::custom)?;
let application_id = match map.remove("application_id") {
Some(v) => from_value::<Option<ApplicationId>>(v).map_err(DeError::custom)?,
None => None,
};
let channels = map
.remove("channels")
.ok_or_else(|| DeError::custom("expected guild channels"))
.and_then(deserialize_guild_channels)
.map_err(DeError::custom)?;
let default_message_notifications = map
.remove("default_message_notifications")
.ok_or_else(|| DeError::custom("expected guild default_message_notifications"))
.and_then(DefaultMessageNotificationLevel::deserialize)
.map_err(DeError::custom)?;
let emojis = map
.remove("emojis")
.ok_or_else(|| DeError::custom("expected guild emojis"))
.and_then(emojis::deserialize)
.map_err(DeError::custom)?;
let explicit_content_filter = map
.remove("explicit_content_filter")
.ok_or_else(|| DeError::custom("expected guild explicit_content_filter"))
.and_then(ExplicitContentFilter::deserialize)
.map_err(DeError::custom)?;
let features = map
.remove("features")
.ok_or(Error::Other("expected guild features"))
.and_then(from_value::<Vec<String>>)
.map_err(DeError::custom)?;
let icon = match map.remove("icon") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let id = map
.remove("id")
.ok_or_else(|| DeError::custom("expected guild id"))
.and_then(GuildId::deserialize)
.map_err(DeError::custom)?;
let joined_at = map
.remove("joined_at")
.ok_or_else(|| DeError::custom("expected guild joined_at"))
.and_then(Timestamp::deserialize)
.map_err(DeError::custom)?;
let large = map
.remove("large")
.ok_or_else(|| DeError::custom("expected guild large"))
.and_then(bool::deserialize)
.map_err(DeError::custom)?;
let member_count = map
.remove("member_count")
.ok_or_else(|| DeError::custom("expected guild member_count"))
.and_then(u64::deserialize)
.map_err(DeError::custom)?;
let members = map
.remove("members")
.ok_or_else(|| DeError::custom("expected guild members"))
.and_then(deserialize_members)
.map_err(DeError::custom)?;
let mfa_level = map
.remove("mfa_level")
.ok_or_else(|| DeError::custom("expected guild mfa_level"))
.and_then(MfaLevel::deserialize)
.map_err(DeError::custom)?;
let name = map
.remove("name")
.ok_or_else(|| DeError::custom("expected guild name"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
let owner_id = map
.remove("owner_id")
.ok_or_else(|| DeError::custom("expected guild owner_id"))
.and_then(UserId::deserialize)
.map_err(DeError::custom)?;
let presences = map
.remove("presences")
.ok_or_else(|| DeError::custom("expected guild presences"))
.and_then(presences::deserialize)
.map_err(DeError::custom)?;
let roles = map
.remove("roles")
.ok_or_else(|| DeError::custom("expected guild roles"))
.and_then(roles::deserialize)
.map_err(DeError::custom)?;
let splash = match map.remove("splash") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let system_channel_id = match map.remove("system_channel_id") {
Some(v) => Option::<ChannelId>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let verification_level = map
.remove("verification_level")
.ok_or_else(|| DeError::custom("expected guild verification_level"))
.and_then(VerificationLevel::deserialize)
.map_err(DeError::custom)?;
let voice_states = map
.remove("voice_states")
.ok_or_else(|| DeError::custom("expected guild voice_states"))
.and_then(deserialize_voice_states)
.map_err(DeError::custom)?;
let description = match map.remove("description") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let premium_tier = match map.remove("premium_tier") {
Some(v) => PremiumTier::deserialize(v).map_err(DeError::custom)?,
None => PremiumTier::default(),
};
let premium_subscription_count = match map.remove("premium_subscription_count") {
#[cfg(not(feature = "simd_json"))]
Some(Value::Null) | None => 0,
#[cfg(feature = "simd_json")]
Some(Value::Static(StaticNode::Null)) | None => 0,
Some(v) => u64::deserialize(v).map_err(DeError::custom)?,
};
let banner = match map.remove("banner") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let vanity_url_code = match map.remove("vanity_url_code") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let preferred_locale = map
.remove("preferred_locale")
.ok_or_else(|| DeError::custom("expected preferred locale"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
let welcome_screen = map
.remove("welcome_screen")
.map(GuildWelcomeScreen::deserialize)
.transpose()
.map_err(DeError::custom)?;
let approximate_member_count = map
.remove("approximate_member_count")
.map(u64::deserialize)
.transpose()
.map_err(DeError::custom)?;
let approximate_presence_count = map
.remove("approximate_presence_count")
.map(u64::deserialize)
.transpose()
.map_err(DeError::custom)?;
let max_video_channel_users = map
.remove("max_video_channel_users")
.map(u64::deserialize)
.transpose()
.map_err(DeError::custom)?;
let max_presences = match map.remove("max_presences") {
Some(v) => Option::<u64>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let max_members =
map.remove("max_members").map(u64::deserialize).transpose().map_err(DeError::custom)?;
let discovery_splash = match map.remove("discovery_splash") {
Some(v) => Option::<String>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let nsfw_level = map
.remove("nsfw_level")
.ok_or_else(|| DeError::custom("expected nsfw_level"))
.and_then(NsfwLevel::deserialize)
.map_err(DeError::custom)?;
let widget_enabled = match map.remove("widget_enabled") {
Some(v) => Option::<bool>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let widget_channel_id = match map.remove("widget_channel_id") {
Some(v) => Option::<ChannelId>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let rules_channel_id = match map.remove("rules_channel_id") {
Some(v) => Option::<ChannelId>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let public_updates_channel_id = match map.remove("public_updates_channel_id") {
Some(v) => Option::<ChannelId>::deserialize(v).map_err(DeError::custom)?,
None => None,
};
let system_channel_flags = map
.remove("system_channel_flags")
.ok_or_else(|| DeError::custom("expected system_channel_flags"))
.and_then(SystemChannelFlags::deserialize)
.map_err(DeError::custom)?;
let stage_instances = match map.remove("stage_instances") {
Some(v) => Vec::<StageInstance>::deserialize(v).map_err(DeError::custom)?,
None => Vec::new(),
};
let threads = match map.remove("threads") {
Some(v) => Vec::<GuildChannel>::deserialize(v).map_err(DeError::custom)?,
None => Vec::new(),
};
let stickers = map
.remove("stickers")
.ok_or_else(|| DeError::custom("expected guild stickers"))
.and_then(stickers::deserialize)
.map_err(DeError::custom)?;
Ok(Self {
afk_channel_id,
afk_timeout,
application_id,
channels,
default_message_notifications,
emojis,
explicit_content_filter,
features,
icon,
id,
joined_at,
large,
member_count,
members,
mfa_level,
name,
owner_id,
presences,
roles,
splash,
discovery_splash,
system_channel_id,
system_channel_flags,
rules_channel_id,
public_updates_channel_id,
verification_level,
voice_states,
description,
premium_tier,
premium_subscription_count,
banner,
vanity_url_code,
preferred_locale,
welcome_screen,
approximate_member_count,
approximate_presence_count,
nsfw_level,
max_video_channel_users,
max_presences,
max_members,
widget_enabled,
widget_channel_id,
stage_instances,
threads,
stickers,
})
}
}
#[cfg(feature = "model")]
fn contains_case_insensitive(to_look_at: &str, to_find: &str) -> bool {
to_look_at.to_lowercase().contains(&to_find.to_lowercase())
}
#[cfg(feature = "model")]
fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool {
to_look_at.to_lowercase().starts_with(&to_find.to_lowercase())
}
#[cfg(feature = "model")]
fn closest_to_origin(origin: &str, word_a: &str, word_b: &str) -> std::cmp::Ordering {
let value_a = match word_a.find(origin) {
Some(value) => value + word_a.len(),
None => return std::cmp::Ordering::Greater,
};
let value_b = match word_b.find(origin) {
Some(value) => value + word_b.len(),
None => return std::cmp::Ordering::Less,
};
value_a.cmp(&value_b)
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum GuildContainer {
Guild(PartialGuild),
Id(GuildId),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GuildWidget {
pub enabled: bool,
pub channel_id: Option<ChannelId>,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct GuildPrune {
pub pruned: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GuildInfo {
pub id: GuildId,
pub icon: Option<String>,
pub name: String,
pub owner: bool,
pub permissions: Permissions,
}
#[cfg(any(feature = "model", feature = "utils"))]
impl GuildInfo {
#[must_use]
pub fn icon_url(&self) -> Option<String> {
self.icon.as_ref().map(|icon| {
let ext = if icon.starts_with("a_") { "gif" } else { "webp" };
cdn!("/icons/{}/{}.{}", self.id, icon, ext)
})
}
}
impl From<PartialGuild> for GuildContainer {
fn from(guild: PartialGuild) -> GuildContainer {
GuildContainer::Guild(guild)
}
}
impl From<GuildId> for GuildContainer {
fn from(guild_id: GuildId) -> GuildContainer {
GuildContainer::Id(guild_id)
}
}
impl From<u64> for GuildContainer {
fn from(id: u64) -> GuildContainer {
GuildContainer::Id(GuildId(id))
}
}
#[cfg(feature = "model")]
impl InviteGuild {
#[must_use]
pub fn splash_url(&self) -> Option<String> {
self.splash.as_ref().map(|splash| cdn!("/splashes/{}/{}.webp?size=4096", self.id, splash))
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct UnavailableGuild {
pub id: GuildId,
#[serde(default)]
pub unavailable: bool,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum DefaultMessageNotificationLevel {
All = 0,
Mentions = 1,
Unknown = !0,
}
enum_number!(DefaultMessageNotificationLevel {
All,
Mentions
});
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum ExplicitContentFilter {
None = 0,
WithoutRole = 1,
All = 2,
Unknown = !0,
}
enum_number!(ExplicitContentFilter {
None,
WithoutRole,
All
});
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum MfaLevel {
None = 0,
Elevated = 1,
Unknown = !0,
}
enum_number!(MfaLevel {
None,
Elevated
});
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum VerificationLevel {
None = 0,
Low = 1,
Medium = 2,
High = 3,
Higher = 4,
Unknown = !0,
}
enum_number!(VerificationLevel {
None,
Low,
Medium,
High,
Higher
});
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum NsfwLevel {
Default = 0,
Explicit = 1,
Safe = 2,
AgeRestricted = 3,
Unknown = !0,
}
enum_number!(NsfwLevel {
Default,
Explicit,
Safe,
AgeRestricted
});
#[cfg(test)]
mod test {
#[cfg(feature = "model")]
mod model {
use std::collections::*;
use crate::model::prelude::*;
fn gen_user() -> User {
User::default()
}
fn gen_member() -> Member {
let dt = Timestamp::now();
let vec1 = Vec::new();
let u = gen_user();
Member {
deaf: false,
guild_id: GuildId(1),
joined_at: Some(dt),
mute: false,
nick: Some("aaaa".to_string()),
roles: vec1,
user: u,
pending: false,
premium_since: None,
permissions: None,
avatar: None,
communication_disabled_until: None,
}
}
fn gen() -> Guild {
let u = gen_user();
let m = gen_member();
let hm1 = HashMap::new();
let hm2 = HashMap::new();
let vec1 = Vec::new();
let dt = Timestamp::now();
let mut hm3 = HashMap::new();
let hm4 = HashMap::new();
let hm5 = HashMap::new();
let hm6 = HashMap::new();
let hm7 = HashMap::new();
hm3.insert(u.id, m);
let notifications = DefaultMessageNotificationLevel::All;
Guild {
afk_channel_id: Some(ChannelId(0)),
afk_timeout: 0,
channels: hm1,
default_message_notifications: notifications,
emojis: hm2,
features: vec1,
icon: Some("/avatars/210/a_aaa.webp?size=1024".to_string()),
id: GuildId(1),
joined_at: dt,
large: false,
member_count: 1,
members: hm3,
mfa_level: MfaLevel::Elevated,
name: "Spaghetti".to_string(),
owner_id: UserId(210),
presences: hm4,
roles: hm5,
splash: Some("asdf".to_string()),
verification_level: VerificationLevel::None,
voice_states: hm6,
description: None,
premium_tier: PremiumTier::Tier1,
application_id: Some(ApplicationId(0)),
explicit_content_filter: ExplicitContentFilter::None,
system_channel_id: Some(ChannelId(0)),
system_channel_flags: SystemChannelFlags::default(),
rules_channel_id: None,
premium_subscription_count: 12,
banner: None,
vanity_url_code: Some("bruhmoment".to_string()),
preferred_locale: "en-US".to_string(),
welcome_screen: None,
approximate_member_count: None,
approximate_presence_count: None,
nsfw_level: NsfwLevel::Default,
max_video_channel_users: None,
max_presences: None,
max_members: None,
widget_enabled: Some(false),
discovery_splash: None,
widget_channel_id: None,
public_updates_channel_id: None,
stage_instances: vec![],
threads: vec![],
stickers: hm7,
}
}
#[test]
fn member_named_username() {
let guild = gen();
let lhs = guild.member_named("test#1432").unwrap().display_name();
assert_eq!(lhs, gen_member().display_name());
}
#[test]
fn member_named_nickname() {
let guild = gen();
let lhs = guild.member_named("aaaa").unwrap().display_name();
assert_eq!(lhs, gen_member().display_name());
}
}
}