Skip to content

Commit

Permalink
date serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
charxene committed Jun 2, 2022
1 parent bb8d267 commit 79e5813
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 50 deletions.
40 changes: 40 additions & 0 deletions Sources/DiscordKit/APIUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@

import Foundation

let iso8601 = { () -> ISO8601DateFormatter in
let fmt = ISO8601DateFormatter()
fmt.formatOptions = [.withInternetDateTime]
return fmt
}()

let iso8601WithFractionalSeconds = { () -> ISO8601DateFormatter in
let fmt = ISO8601DateFormatter()
fmt.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return fmt
}()

public extension DiscordAPI {
/// Populate a ``GatewayConnProperties`` struct with some constant
/// values + some dynamic versions
Expand Down Expand Up @@ -48,4 +60,32 @@ public extension DiscordAPI {
static var userAgent: String {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) discord/\(GatewayConfig.default.parity.version) Chrome/91.0.4472.164 Electron/\(GatewayConfig.default.parity.electronVersion) Safari/537.36"
}

static func encoder() -> JSONEncoder {
let enc = JSONEncoder()
enc.dateEncodingStrategy = .custom({ date, encoder in
var container = encoder.singleValueContainer()
let dateString = iso8601WithFractionalSeconds.string(from: date)
try container.encode(dateString)
})
return enc
}

static func decoder() -> JSONDecoder {
let dec = JSONDecoder()
dec.dateDecodingStrategy = .custom({ decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)

if let date = iso8601.date(from: dateString) {
return date
}
if let date = iso8601WithFractionalSeconds.date(from: dateString) {
return date
}

throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
})
return dec
}
}
6 changes: 3 additions & 3 deletions Sources/DiscordKit/Gateway/RobustWebSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ public class RobustWebSocket: NSObject, ObservableObject {

let decoded: GatewayIncoming
do {
decoded = try JSONDecoder().decode(GatewayIncoming.self, from: message.data(using: .utf8) ?? Data())
decoded = try DiscordAPI.decoder().decode(GatewayIncoming.self, from: message.data(using: .utf8) ?? Data())
} catch {
print(error)
print(message)
//print(message)
return
}

Expand Down Expand Up @@ -485,7 +485,7 @@ public extension RobustWebSocket {
guard connected else { return }

let sendPayload = GatewayOutgoing(op: op, d: data, s: seq)
guard let encoded = try? JSONEncoder().encode(sendPayload)
guard let encoded = try? DiscordAPI.encoder().encode(sendPayload)
else { return }

log.debug("Outgoing Payload: <\(String(describing: op), privacy: .public)> \(String(describing: data), privacy: .sensitive(mask: .hash)) [seq: \(String(describing: self.seq), privacy: .public)]")
Expand Down
8 changes: 4 additions & 4 deletions Sources/DiscordKit/Objects/Channel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public struct Channel: Identifiable, Codable, GatewayData, Equatable {
public let owner_id: Snowflake?
public let application_id: Snowflake?
public let parent_id: Snowflake? // ID of parent category (for channels) or parent channel (for threads)
public let last_pin_timestamp: ISOTimestamp?
public let last_pin_timestamp: Date?
public let rtc_region: String?
public let video_quality_mode: VideoQualityMode?
public let message_count: Int? // Approx. msg count in threads, stops counting at 50
Expand All @@ -70,16 +70,16 @@ public struct Channel: Identifiable, Codable, GatewayData, Equatable {
public struct ThreadMeta: Codable {
public let archived: Bool
public let auto_archive_duration: Int // Duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080
public let archive_timestamp: ISOTimestamp
public let archive_timestamp: Date
public let locked: Bool
public let invitable: Bool? // Only available in private threads
public let create_timestamp: ISOTimestamp? // Timestamp when the thread was created; only populated for threads created after 2022-01-09
public let create_timestamp: Date? // Timestamp when the thread was created; only populated for threads created after 2022-01-09
}

public struct ThreadMember: Codable, GatewayData {
public let id: Snowflake? // ID of thread
public let user_id: Snowflake? // ID of user
public let join_timestamp: ISOTimestamp // When user last joined thread
public let join_timestamp: Date // When user last joined thread
public let flags: Int // Any user-thread settings, currently only used for notifications
public let guild_id: Snowflake?
}
4 changes: 2 additions & 2 deletions Sources/DiscordKit/Objects/Embed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public struct Embed: Codable, Identifiable {
public let type: EmbedType?
public let description: String?
public let url: String?
public let timestamp: ISOTimestamp?
public let timestamp: Date?
public let color: Int?
public let footer: EmbedFooter?
public let image: EmbedMedia?
Expand All @@ -31,7 +31,7 @@ public struct Embed: Codable, Identifiable {
public let author: EmbedAuthor?
public let fields: [EmbedField]?
public var id: String {
"\(title ?? "")\(description ?? "")\(url ?? "")\(String(color ?? 0))\(timestamp ?? "")"
"\(title ?? "")\(description ?? "")\(url ?? "")\(String(color ?? 0))\(String(timestamp?.timeIntervalSince1970 ?? 0))"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ import Foundation
public struct ChannelPinsUpdate: Codable, GatewayData {
public let guild_id: Snowflake?
public let channel_id: Snowflake
public let last_pin_timestamp: ISOTimestamp?
public let last_pin_timestamp: Date?
}
6 changes: 3 additions & 3 deletions Sources/DiscordKit/Objects/Gateway/Event/GuildMemberEvt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public struct GuildMemberUpdate: Codable, GatewayData {
public let user: User
public let nick: String?
public let avatar: String? // User's guild avatar hash
public let joined_at: ISOTimestamp?
public let premium_since: ISOTimestamp? // When user started boosting guild
public let joined_at: Date?
public let premium_since: Date? // When user started boosting guild
public let deaf: Bool?
public let mute: Bool?
public let pending: Bool?
public let communication_disabled_until: ISOTimestamp?
public let communication_disabled_until: Date?
}
8 changes: 4 additions & 4 deletions Sources/DiscordKit/Objects/Guild.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public enum GuildFeature: String, Codable {
}

public struct Guild: GatewayData, Equatable, Identifiable {
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: ISOTimestamp? = 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) {
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) {
self.id = id
self.name = name
self.icon = icon
Expand Down Expand Up @@ -122,7 +122,7 @@ public struct Guild: GatewayData, Equatable, Identifiable {
public let system_channel_id: Snowflake? // ID of channel for system-created messages
public let system_channel_flags: Int
public let rules_channel_id: Snowflake?
public var joined_at: ISOTimestamp?
public var joined_at: Date?
public var large: Bool?
public var unavailable: Bool? // If guild is unavailable due to an outage
public var member_count: Int?
Expand Down Expand Up @@ -205,8 +205,8 @@ public struct GuildScheduledEvent: Codable, GatewayData {
public let creator_id: Snowflake?
public let name: String
public let description: String?
public let scheduled_start_time: ISOTimestamp
public let scheduled_end_time: ISOTimestamp?
public let scheduled_start_time: Date
public let scheduled_end_time: Date?
public let privacy_level: GuildScheduledEvtPrivacyLvl
public let status: GuildScheduledEvtStatus
public let entity_type: GuildScheduledEvtEntityType
Expand Down
15 changes: 0 additions & 15 deletions Sources/DiscordKit/Objects/ISOTimestamp.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/DiscordKit/Objects/Integration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public struct Integration: Codable, GatewayData {
public let expire_grace_period: Int? // The grace period (in days) before expiring subscribers
public let user: User?
public let account: IntegrationAccount
public let synced_at: ISOTimestamp?
public let synced_at: Date?
public let subscriber_count: Int?
public let revoked: Bool?
public let application: IntegrationApplication?
Expand Down
6 changes: 3 additions & 3 deletions Sources/DiscordKit/Objects/Member.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ public struct Member: Codable, GatewayData {
public let nick: String?
public let avatar: String?
public let roles: [Snowflake]
public let joined_at: ISOTimestamp
public let premium_since: ISOTimestamp? // When the user started boosting the guild
public let joined_at: Date
public let premium_since: Date? // When the user started boosting the guild
public let deaf: Bool
public let mute: Bool
public let pending: Bool?
public let permissions: String? // Total permissions of the member in the channel, including overwrites, returned when in the interaction object
public let communication_disabled_until: ISOTimestamp? // When the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out
public let communication_disabled_until: Date? // When the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out
public let guild_id: Snowflake?
}
12 changes: 6 additions & 6 deletions Sources/DiscordKit/Objects/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ public struct Message: Codable, GatewayData, Equatable {
public var content: String

/// When this message was sent
public let timestamp: ISOTimestamp
public let timestamp: Date

/// When this message was edited (or null if never)
public var edited_timestamp: ISOTimestamp?
public var edited_timestamp: Date?

/// If this was a TTS message
public var tts: Bool

Expand Down Expand Up @@ -191,8 +191,8 @@ public struct PartialMessage: Codable, GatewayData {
public let author: User?
public let member: Member?
public let content: String? // The message contents (up to 2000 characters)
public let timestamp: ISOTimestamp?
public let edited_timestamp: ISOTimestamp?
public let timestamp: Date?
public let edited_timestamp: Date?
public let tts: Bool?
public let mention_everyone: Bool?
public let mentions: [User]?
Expand Down
6 changes: 3 additions & 3 deletions Sources/DiscordKit/Objects/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public struct CurrentUser: Codable, GatewayData, Equatable {
/// > Warning: The user profile endpoint is undocumented, and this struct
/// > was created purely from reverse engineering and observations.
public struct UserProfile: Codable, GatewayData {
public init(connected_accounts: [Connection], guild_member: Member?, premium_guild_since: ISOTimestamp?, premium_since: ISOTimestamp?, mutual_guilds: [MutualGuild]?, user: User) {
public init(connected_accounts: [Connection], guild_member: Member?, premium_guild_since: Date?, premium_since: Date?, mutual_guilds: [MutualGuild]?, user: User) {
self.connected_accounts = connected_accounts
self.guild_member = guild_member
self.premium_guild_since = premium_guild_since
Expand All @@ -163,8 +163,8 @@ public struct UserProfile: Codable, GatewayData {

public let connected_accounts: [Connection]
public let guild_member: Member?
public let premium_guild_since: ISOTimestamp?
public let premium_since: ISOTimestamp?
public let premium_guild_since: Date?
public let premium_since: Date?
public let mutual_guilds: [MutualGuild]?

/// A more complete ``User`` struct, containing the user's bio, among others.
Expand Down
10 changes: 5 additions & 5 deletions Sources/DiscordKit/REST/APIRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public extension DiscordAPI {

req.setValue(Locale.englishUS.rawValue, forHTTPHeaderField: "x-discord-locale")
req.setValue("bugReporterEnabled", forHTTPHeaderField: "x-debug-options")
guard let superEncoded = try? JSONEncoder().encode(getSuperProperties()) else {
guard let superEncoded = try? DiscordAPI.encoder().encode(getSuperProperties()) else {
DiscordAPI.log.error("Couldn't encode super properties, something is seriously wrong")
return nil
}
Expand Down Expand Up @@ -118,9 +118,9 @@ public extension DiscordAPI {
guard let d = try? await makeRequest(path: path, query: query)
else { return nil }

return try JSONDecoder().decode(T.self, from: d)
return try DiscordAPI.decoder().decode(T.self, from: d)
} catch let DecodingError.dataCorrupted(context) {
print(context)
print(context.debugDescription)
} catch let DecodingError.keyNotFound(key, context) {
print("Key '\(key)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
Expand All @@ -142,7 +142,7 @@ public extension DiscordAPI {
body: B? = nil,
attachments: [URL] = []
) async -> D? {
let p = body != nil ? try? JSONEncoder().encode(body) : nil
let p = body != nil ? try? DiscordAPI.encoder().encode(body) : nil
guard let d = try? await makeRequest(
path: path,
attachments: attachments,
Expand All @@ -151,7 +151,7 @@ public extension DiscordAPI {
)
else { return nil }

return try? JSONDecoder().decode(D.self, from: d)
return try? DiscordAPI.decoder().decode(D.self, from: d)
}

/// Make a `POST` request to the Discord REST API, for endpoints
Expand Down

0 comments on commit 79e5813

Please sign in to comment.