Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion twilight-gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
};
use tokio_websockets::Connector;
use twilight_model::gateway::{
Intents,
Capabilities, Intents,
payload::outgoing::{identify::IdentifyProperties, update_presence::UpdatePresencePayload},
};

Expand Down Expand Up @@ -40,6 +40,8 @@ impl Debug for Token {
/// [`From<Config>`] implementation and then rebuilding it into a rew config.
#[derive(Clone, Debug)]
pub struct Config<Q = InMemoryQueue> {
/// Capabilities that the shard opts into when identifying with the gateway.
capabilities: Capabilities,
/// Identification properties the shard will use.
identify_properties: Option<IdentifyProperties>,
/// Intents that the shard requests when identifying with the gateway.
Expand Down Expand Up @@ -84,6 +86,11 @@ impl Config {
}

impl<Q> Config<Q> {
/// Capabilities that the shard opts into when identifying with the gateway.
pub const fn capabilities(&self) -> Capabilities {
self.capabilities
}

/// Immutable reference to the identification properties the shard will use.
pub const fn identify_properties(&self) -> Option<&IdentifyProperties> {
self.identify_properties.as_ref()
Expand Down Expand Up @@ -166,6 +173,7 @@ impl ConfigBuilder {

Self {
inner: Config {
capabilities: Capabilities::empty(),
identify_properties: None,
intents,
large_threshold: 50,
Expand Down Expand Up @@ -324,6 +332,7 @@ impl<Q> ConfigBuilder<Q> {
/// turns itself into a no-op.
pub fn queue<NewQ>(self, queue: NewQ) -> ConfigBuilder<NewQ> {
let Config {
capabilities,
identify_properties,
intents,
large_threshold,
Expand All @@ -339,6 +348,7 @@ impl<Q> ConfigBuilder<Q> {

ConfigBuilder {
inner: Config {
capabilities,
identify_properties,
intents,
large_threshold,
Expand Down
1 change: 1 addition & 0 deletions twilight-gateway/src/shard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ impl<Q: Queue> Shard<Q> {

self.pending = Pending::event(
Identify::new(IdentifyInfo {
capabilities: self.config.capabilities(),
compress: false,
intents: self.config.intents(),
large_threshold: self.config.large_threshold(),
Expand Down
6 changes: 6 additions & 0 deletions twilight-http/src/request/guild/get_guild_channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ use twilight_model::{
};

/// Get the channels in a guild.
///
/// # Channel Obfuscation
///
/// Channels the user does not have access to will not be returned via this
/// endpoint once Discord finalizes the rollout of Channel Obfuscation on or
/// around November 16th, 2026.
#[must_use = "requests must be configured and executed"]
pub struct GetGuildChannels<'a> {
guild_id: Id<GuildMarker>,
Expand Down
7 changes: 7 additions & 0 deletions twilight-model/src/channel/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ bitflags! {
const REQUIRE_TAG = 1 << 4;
/// Hide the download options for this post in a media channel.
const HIDE_MEDIA_DOWNLOAD_OPTIONS = 1 << 15;
/// This channel's metadata has been obfuscated because the current user
/// cannot view it.
///
/// Only ever set on channels received over the Gateway; the HTTP API
/// never sets this flag.
const CHANNEL_OBFUSCATED = 1 << 17;
}
}

Expand Down Expand Up @@ -74,6 +80,7 @@ mod tests {
);
const_assert_eq!(ChannelFlags::PINNED.bits(), 1 << 1);
const_assert_eq!(ChannelFlags::REQUIRE_TAG.bits(), 1 << 4);
const_assert_eq!(ChannelFlags::CHANNEL_OBFUSCATED.bits(), 1 << 17);

#[test]
fn serde() {
Expand Down
25 changes: 25 additions & 0 deletions twilight-model/src/channel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,32 @@ use serde::{Deserialize, Serialize};
///
/// For Discord's documentation on channels, refer to [Discord Docs/Channel].
///
/// # Obfuscated channels
///
/// Guild channels which a user can not access are referred to as "obfuscated
/// channels". Obfuscated channels can be opted in to via the
/// [`CHANNEL_OBFUSCATION`][crate::gateway::Capabilities::CHANNEL_OBFUSCATION]
/// capability prior to its enforcement by Discord on November 16th, 2026; once
/// released, obfuscated channels will be enforced for all users.
///
/// Via the HTTP API obfuscated channels aren't received.
///
/// Via the Gateway obfuscated channels are received with the
/// [`ChannelFlags::CHANNEL_OBFUSCATED`] flag set in the [`flags`][Self::flags]
/// field. Fields such as [`name`][Self::name] will be obfuscated with values
/// such as `"___hidden___"`. The [`id`][Self::id], [`kind`][Self::kind],
/// [`position`][Self::position], and [`parent_id`][Self::parent_id] fields
/// won't be obfuscated and fields other than these should not be relied upon.
/// Obfuscated channels will have a single
/// [permission overwrite][permission_overwrite::PermissionOverwrite] denying
/// the [`VIEW_CHANNEL`][`Permissions::VIEW_CHANNEL`] permission to the guild's
/// `@everyone` role.
///
/// Refer to [Discord Docs/Channel Obfuscation].
///
/// [`Permissions::VIEW_CHANNEL`]: crate::guild::Permissions::VIEW_CHANNEL
/// [Discord Docs/Channel]: https://discord.com/developers/docs/resources/channel
/// [Discord Docs/Channel Obfuscation]: https://docs.discord.com/developers/resources/channel#channel-object-obfuscated-channels
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Channel {
/// ID of the application that created the channel.
Expand Down
99 changes: 99 additions & 0 deletions twilight-model/src/gateway/capabilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use bitflags::bitflags;
use serde::{
de::{Deserialize, Deserializer},
ser::{Serialize, Serializer},
};

bitflags! {
/// Gateway capabilities.
///
/// Developers may specify capabilities when connecting to the gateway.
/// Capabilities allow applications to opt-in to gateway behavior. To
/// specify multiple capabilities, create a union using the `|` operator. See
/// [Discord Docs/Capabilities].
///
/// [Discord Docs/Capabilities]: https://discord.com/developers/docs/topics/gateway#identify-gateway-capabilities
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Capabilities: u64 {
/// Opts the client into receiving obfuscated channel metadata over the
/// Gateway for channels it can't view.
///
/// Discord plans to fully roll out Channel Obfuscation for all users on
/// November 16th, 2026, at which point specifying this capability would
/// have no effect.
///
/// See [Discord Docs/Channel Obfuscation].
///
/// [Discord Docs/Channel Obfuscation]: https://docs.discord.com/developers/resources/channel#channel-object-obfuscated-channels
const CHANNEL_OBFUSCATION = 1 << 15;
Comment thread
Erk- marked this conversation as resolved.
}
}

impl<'de> Deserialize<'de> for Capabilities {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::from_bits_truncate(u64::deserialize(deserializer)?))
}
}

impl Serialize for Capabilities {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u64(self.bits())
}
}

#[cfg(test)]
mod tests {
use super::Capabilities;
use serde::{Deserialize, Serialize};
use serde_test::Token;
use static_assertions::{assert_impl_all, const_assert_eq};
use std::{
fmt::{Binary, Debug, LowerHex, Octal, UpperHex},
hash::Hash,
ops::{
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Sub, SubAssign,
},
};

assert_impl_all!(
Capabilities: Binary,
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
Clone,
Copy,
Debug,
Deserialize<'static>,
Eq,
Extend<Capabilities>,
FromIterator<Capabilities>,
Hash,
LowerHex,
Not,
Octal,
PartialEq,
Send,
Serialize,
Sub,
SubAssign,
Sync,
UpperHex
);
const_assert_eq!(Capabilities::CHANNEL_OBFUSCATION.bits(), 1 << 15);

#[test]
fn serde() {
serde_test::assert_tokens(
&Capabilities::CHANNEL_OBFUSCATION,
&[Token::U64(Capabilities::CHANNEL_OBFUSCATION.bits())],
);
// Deserialization truncates unknown bits.
serde_test::assert_de_tokens(&Capabilities::empty(), &[Token::U64(1 << 63)]);
}
}
2 changes: 2 additions & 0 deletions twilight-model/src/gateway/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod event;
pub mod payload;
pub mod presence;

mod capabilities;
mod close_code;
mod frame;
mod id;
Expand All @@ -12,6 +13,7 @@ mod reaction;
mod session_start_limit;

pub use self::{
capabilities::Capabilities,
close_code::{CloseCode, CloseCodeConversionError},
frame::CloseFrame,
id::{ShardId, ShardIdParseError, ShardIdParseErrorType},
Expand Down
8 changes: 8 additions & 0 deletions twilight-model/src/gateway/payload/incoming/channel_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ use crate::channel::Channel;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};

/// A channel has been updated.
///
/// # Channel obfuscation
///
/// A guild channel which a user does not have access to may be obfuscated. Once
/// the current user has access to a guild channel the previously obfuscated
/// fields will have their true values revealed. Refer to the documentation for
/// [`Channel`] for more information on channel obfuscation.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ChannelUpdate(pub Channel);

Expand Down
3 changes: 2 additions & 1 deletion twilight-model/src/gateway/payload/outgoing/identify.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::update_presence::UpdatePresencePayload;
use crate::gateway::{ShardId, intents::Intents, opcode::OpCode};
use crate::gateway::{Capabilities, ShardId, intents::Intents, opcode::OpCode};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
Expand All @@ -19,6 +19,7 @@ impl Identify {

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct IdentifyInfo {
pub capabilities: Capabilities,
pub compress: bool,
pub intents: Intents,
pub large_threshold: u64,
Expand Down
Loading