Skip to content

Commit 13552ae

Browse files
fix(gateway): safely handle more JSON decode failures
more liberal use of DecodeThrowable hopefully reduces the likelihood of ready event decode failures
1 parent 6b5d0ca commit 13552ae

File tree

6 files changed

+33
-18
lines changed

6 files changed

+33
-18
lines changed

Sources/DiscordKitCore/Objects/Data/Guild.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public enum GuildFeature: String, Codable {
3838
}
3939

4040
public struct Guild: GatewayData, Equatable, Identifiable {
41-
public init(id: Snowflake, name: String, icon: String? = nil, icon_hash: String? = nil, splash: String? = nil, discovery_splash: String? = nil, owner: Bool? = nil, owner_id: Snowflake, permissions: String? = nil, region: String? = nil, afk_channel_id: Snowflake? = nil, afk_timeout: Int, widget_enabled: Bool? = nil, widget_channel_id: Snowflake? = nil, verification_level: VerificationLevel, default_message_notifications: MessageNotifLevel, explicit_content_filter: ExplicitContentFilterLevel, roles: [DecodableThrowable<Role>], emojis: [DecodableThrowable<Emoji>], features: [DecodableThrowable<GuildFeature>], mfa_level: MFALevel, application_id: Snowflake? = nil, system_channel_id: Snowflake? = nil, system_channel_flags: Int, rules_channel_id: Snowflake? = nil, joined_at: Date? = nil, large: Bool? = nil, unavailable: Bool? = nil, member_count: Int? = nil, voice_states: [VoiceState]? = nil, members: [Member]? = nil, channels: [Channel]? = nil, threads: [Channel]? = nil, presences: [PresenceUpdate]? = nil, max_presences: Int? = nil, max_members: Int? = nil, vanity_url_code: String? = nil, description: String? = nil, banner: String? = nil, premium_tier: PremiumLevel, premium_subscription_count: Int? = nil, preferred_locale: Locale, public_updates_channel_id: Snowflake? = nil, max_video_channel_users: Int? = nil, approximate_member_count: Int? = nil, approximate_presence_count: Int? = nil, welcome_screen: GuildWelcomeScreen? = nil, nsfw_level: NSFWLevel, stage_instances: [StageInstance]? = nil, stickers: [Sticker]? = nil, guild_scheduled_events: [GuildScheduledEvent]? = nil, premium_progress_bar_enabled: Bool) {
41+
public init(id: Snowflake, name: String, icon: String? = nil, icon_hash: String? = nil, splash: String? = nil, discovery_splash: String? = nil, owner: Bool? = nil, owner_id: Snowflake, permissions: String? = nil, region: String? = nil, afk_channel_id: Snowflake? = nil, afk_timeout: Int, widget_enabled: Bool? = nil, widget_channel_id: Snowflake? = nil, verification_level: VerificationLevel, default_message_notifications: MessageNotifLevel, explicit_content_filter: ExplicitContentFilterLevel, roles: [DecodeThrowable<Role>], emojis: [DecodeThrowable<Emoji>], features: [DecodeThrowable<GuildFeature>], mfa_level: MFALevel, application_id: Snowflake? = nil, system_channel_id: Snowflake? = nil, system_channel_flags: Int, rules_channel_id: Snowflake? = nil, joined_at: Date? = nil, large: Bool? = nil, unavailable: Bool? = nil, member_count: Int? = nil, voice_states: [VoiceState]? = nil, members: [Member]? = nil, channels: [Channel]? = nil, threads: [Channel]? = nil, presences: [PresenceUpdate]? = nil, max_presences: Int? = nil, max_members: Int? = nil, vanity_url_code: String? = nil, description: String? = nil, banner: String? = nil, premium_tier: PremiumLevel, premium_subscription_count: Int? = nil, preferred_locale: Locale, public_updates_channel_id: Snowflake? = nil, max_video_channel_users: Int? = nil, approximate_member_count: Int? = nil, approximate_presence_count: Int? = nil, welcome_screen: GuildWelcomeScreen? = nil, nsfw_level: NSFWLevel, stage_instances: [StageInstance]? = nil, stickers: [Sticker]? = nil, guild_scheduled_events: [GuildScheduledEvent]? = nil, premium_progress_bar_enabled: Bool) {
4242
self.id = id
4343
self.name = name
4444
self.icon = icon
@@ -109,7 +109,7 @@ public struct Guild: GatewayData, Equatable, Identifiable {
109109
public let verification_level: VerificationLevel
110110
public let default_message_notifications: MessageNotifLevel
111111
public let explicit_content_filter: ExplicitContentFilterLevel
112-
public let features: [DecodableThrowable<GuildFeature>]
112+
public let features: [DecodeThrowable<GuildFeature>]
113113
public let mfa_level: MFALevel
114114
public let application_id: Snowflake? // For bot-created guilds
115115
public let system_channel_id: Snowflake? // ID of channel for system-created messages
@@ -149,8 +149,8 @@ public struct PreloadedGuild: GatewayData, Identifiable, Equatable {
149149
}
150150

151151
public let version: Int
152-
public let channels: [Channel]
153-
public let emojis: [DecodableThrowable<Emoji>]
152+
public let channels: [DecodeThrowable<Channel>]
153+
public let emojis: [DecodeThrowable<Emoji>]
154154
public let id: Snowflake
155155
public let joined_at: Date
156156
public let large: Bool
@@ -159,19 +159,19 @@ public struct PreloadedGuild: GatewayData, Identifiable, Equatable {
159159
/// Members in the guild
160160
///
161161
/// > User accounts will only receive the client's member and users in voice channels, and
162-
public let members: [Member]?
162+
public let members: [DecodeThrowable<Member>]?
163163

164164
/// Number of "boosts" the server has
165165
public let premium_subscription_count: Int
166166
public let properties: Guild
167-
public let roles: [DecodableThrowable<Role>]
168-
public let stickers: [Sticker]
167+
public let roles: [DecodeThrowable<Role>]
168+
public let stickers: [DecodeThrowable<Sticker>]
169169
// public let threads:
170170

171171
/// Convenience init for creating DM channel
172172
public init(channels: [Channel], properties: Guild) {
173173
self.version = 0
174-
self.channels = channels
174+
self.channels = channels.map { DecodeThrowable($0) }
175175
self.emojis = []
176176
self.id = "@me"
177177
self.joined_at = .distantPast

Sources/DiscordKitCore/Objects/Data/Sticker.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import Foundation
99

1010
public enum StickerType: Int, Codable {
1111
case standard = 1
12-
case guild = 2
12+
case guild
1313
}
1414

1515
public enum StickerFormat: Int, Codable {
1616
case png = 1
17-
case aPNG = 2 // Animated PNG
18-
case lottie = 3
17+
case aPNG // Animated PNG
18+
case lottie
19+
case gif
1920
}
2021

21-
public struct Sticker: Codable, GatewayData {
22+
public struct Sticker: Codable, GatewayData, Identifiable {
2223
public let id: Snowflake
2324
public let pack_id: Snowflake? // For standard stickers, id of the pack the sticker is from
2425
public let name: String
@@ -40,7 +41,7 @@ public struct StickerItem: Codable, Identifiable {
4041
public let format_type: StickerFormat
4142
}
4243

43-
public struct StickerPack: Codable, GatewayData {
44+
public struct StickerPack: Codable, GatewayData, Identifiable {
4445
public let id: Snowflake
4546
public let stickers: [StickerItem]
4647
public let name: String

Sources/DiscordKitCore/Objects/Gateway/Event/ReadyEvt.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public struct ReadyEvt: Decodable, GatewayData {
1313
public let v: Int
1414
public let user: CurrentUser
1515
public let users: [User]
16-
public let guilds: [PreloadedGuild]
16+
public let guilds: [DecodeThrowable<PreloadedGuild>]
1717
public let session_id: String
1818
public let user_settings: UserSettings? // Depreciated, no longer sent
1919
/// Protobuf of user settings
2020
public let user_settings_proto: String
2121
/// DMs for this user
22-
public let private_channels: [Channel]
22+
public let private_channels: [DecodeThrowable<Channel>]
2323

2424
public let merged_members: [[Member]]
2525

Sources/DiscordKitCore/REST/APICurrentUser.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Foundation
1313
public extension DiscordREST {
1414
// MARK: Get Current User DMs
1515
// GET /users/@me/channels
16-
func getDMs() async throws -> [DecodableThrowable<Channel>] {
16+
func getDMs() async throws -> [DecodeThrowable<Channel>] {
1717
return try await getReq(path: "users/@me/channels")
1818
}
1919

Sources/DiscordKitCore/REST/APIGuild.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public extension DiscordREST {
1313
/// Get Guild Channels
1414
///
1515
/// > GET: `/guilds/{guild.id}/channels`
16-
func getGuildChannels(id: Snowflake) async throws -> [DecodableThrowable<Channel>] {
16+
func getGuildChannels(id: Snowflake) async throws -> [DecodeThrowable<Channel>] {
1717
return try await getReq(path: "guilds/\(id)/channels")
1818
}
1919

Sources/DiscordKitCore/Utils/DecodableThrowable.swift renamed to Sources/DiscordKitCore/Utils/DecodeThrowable.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,26 @@ import Foundation
1313
/// decoding errors to be removed, instead of the whole struct failing
1414
/// to decode. Use a compactMap with `try? result.get()` to remove
1515
/// items that failed to decode.
16-
public struct DecodableThrowable<T: Decodable>: Decodable {
16+
public struct DecodeThrowable<T: Decodable>: Decodable {
1717
/// Decoded result, use `try? .get()` to retrive the value
1818
/// or nil if decoding failed
1919
public let result: Result<T, Error>
2020

21+
public func unwrap() throws -> T {
22+
try result.get()
23+
}
24+
25+
public init(_ wrapped: T) {
26+
result = .success(wrapped)
27+
}
28+
2129
public init(from decoder: Decoder) throws {
2230
result = Result(catching: { try T(from: decoder) })
2331
}
2432
}
33+
34+
public extension Array {
35+
func compactUnwrap<T>() -> [T] where Element == DecodeThrowable<T> {
36+
self.compactMap { try? $0.unwrap() }
37+
}
38+
}

0 commit comments

Comments
 (0)