Skip to content

Commit 50fb7f3

Browse files
feat(guild): guildMemberListUpdate and UpdateGuildSubscriptions gateway payload
For retrieving the member list of a guild and subscribing to other updates.
1 parent cdd577e commit 50fb7f3

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed

Sources/DiscordKitCore/Objects/Gateway/DataStructs.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,33 @@ public struct SubscribeGuildEvts: OutgoingGatewayData {
117117
}
118118
}
119119

120+
/// Guild update subscriptions
121+
public struct UpdateGuildSubscriptions: OutgoingGatewayData {
122+
public struct Subscription: OutgoingGatewayData {
123+
public let activities: Bool?
124+
public let threads: Bool?
125+
public let typing: Bool?
126+
/// Range of members to request from each channel
127+
///
128+
/// - Important: Returned members will only be correctly sorted up to
129+
/// 100 members (e.g. a range of 0-99).
130+
public let channels: [Snowflake : [DiscordRange]]
131+
132+
public init(activities: Bool? = nil, threads: Bool? = nil, typing: Bool? = nil, channels: [Snowflake : [DiscordRange]]) {
133+
self.activities = activities
134+
self.threads = threads
135+
self.typing = typing
136+
self.channels = channels
137+
}
138+
}
139+
140+
public let subscriptions: [Snowflake : Subscription]
141+
142+
public init(subscriptions: [Snowflake : Subscription]) {
143+
self.subscriptions = subscriptions
144+
}
145+
}
146+
120147
/// Current client state, sent with the ``GatewayIdentify`` payload
121148
///
122149
/// > Warning: This should only be sent in identify payloads for user accounts. Bot accounts don't need this!

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public enum GatewayEvent: String, Codable {
4343
case guildMemberRemove = "GUILD_MEMBER_REMOVE"
4444
case guildMemberUpdate = "GUILD_MEMBER_UPDATE"
4545
case guildMembersChunk = "GUILD_MEMBERS_CHUNK"
46+
case guildMemberListUpdate = "GUILD_MEMBER_LIST_UPDATE"
4647
// MARK: Guild Roles
4748
case guildRoleCreate = "GUILD_ROLE_CREATE"
4849
case guildRoleUpdate = "GUILD_ROLE_UPDATE"

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,61 @@ public struct GuildMemberUpdate: Codable, GatewayData {
2828
public let pending: Bool?
2929
public let communication_disabled_until: Date?
3030
}
31+
32+
public struct GuildMemberListUpdate: Decodable, GatewayData {
33+
public struct Group: Decodable, Identifiable, GatewayData {
34+
public let id: Snowflake
35+
public let count: Int
36+
}
37+
38+
public struct Data: Codable {
39+
public struct Group: Codable {
40+
public let id: Snowflake
41+
}
42+
43+
public let member: Member?
44+
public let group: Group?
45+
}
46+
47+
public enum UpdateOp: Decodable, GatewayData {
48+
private enum Op: String, Codable {
49+
case update = "UPDATE"
50+
case sync = "SYNC"
51+
case delete = "DELETE"
52+
case insert = "INSERT"
53+
}
54+
55+
case update(Data, index: Int)
56+
case insert(Data, index: Int)
57+
case delete(Int)
58+
case sync([Data], range: DiscordRange)
59+
60+
enum CodingKeys: CodingKey {
61+
case index
62+
case range
63+
case item
64+
case items
65+
case op
66+
}
67+
68+
public init(from decoder: any Decoder) throws {
69+
let container = try decoder.container(keyedBy: CodingKeys.self)
70+
let op = try container.decode(Op.self, forKey: .op)
71+
switch op {
72+
case .sync:
73+
self = .sync(try container.decode([Data].self, forKey: .items), range: try container.decode(DiscordRange.self, forKey: .range))
74+
case .update, .insert:
75+
self = .update(try container.decode(Data.self, forKey: .item), index: try container.decode(Int.self, forKey: .index))
76+
case .delete:
77+
self = .delete(try container.decode(Int.self, forKey: .index))
78+
}
79+
}
80+
}
81+
82+
public let groups: [Group]
83+
public let guild_id: Snowflake
84+
public let id: String
85+
public let member_count: Int
86+
public let online_count: Int
87+
public let ops: [UpdateOp]
88+
}

Sources/DiscordKitCore/Objects/Gateway/Gateway.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public enum GatewayOutgoingOpcodes: Int, Codable {
3535
case resume = 6 // Attempt to resume disconnected session
3636
case requestGuildMembers = 8
3737
case subscribeGuildEvents = 14
38+
case updateGuildSubscriptions = 37
3839
}
3940

4041
public enum GatewayIncomingOpcodes: Int, Codable {

Sources/DiscordKitCore/Objects/Gateway/GatewayIO.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ public struct GatewayIncoming: Decodable {
8686

8787
/// Guild members chunk
8888
case guildMembersChunk(GuildMembersChunk)
89+
90+
/// Guild member list update
91+
case guildMemberListUpdate(GuildMemberListUpdate)
8992

9093
/// Guild role created
9194
case guildRoleCreate(GuildRoleEvt)
@@ -262,6 +265,7 @@ public struct GatewayIncoming: Decodable {
262265
case .guildUpdate: data = .guildUpdate(try values.decode(Guild.self, forKey: .data))
263266
case .guildDelete: data = .guildDelete(try values.decode(GuildUnavailable.self, forKey: .data))
264267
case .guildMembersChunk: data = .guildMembersChunk(try values.decode(GuildMembersChunk.self, forKey: .data))
268+
case .guildMemberListUpdate: data = .guildMemberListUpdate(try values.decode(GuildMemberListUpdate.self, forKey: .data))
265269
case .guildRoleCreate: data = .guildRoleCreate(try values.decode(GuildRoleEvt.self, forKey: .data))
266270
case .guildRoleUpdate: data = .guildRoleUpdate(try values.decode(GuildRoleEvt.self, forKey: .data))
267271
case .guildRoleDelete: data = .guildRoleDelete(try values.decode(GuildRoleDelete.self, forKey: .data))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// DiscordRange.swift
3+
//
4+
//
5+
// Created by Vincent Kwok on 14/3/24.
6+
//
7+
8+
import Foundation
9+
10+
public struct DiscordRange: Codable {
11+
public let start: Int
12+
public let end: Int
13+
14+
public var closedRange: ClosedRange<Int> { start...end }
15+
16+
public init(start: Int, end: Int) {
17+
self.start = start
18+
self.end = end
19+
}
20+
21+
public init(from decoder: any Decoder) throws {
22+
var container = try decoder.unkeyedContainer()
23+
self.start = try container.decode(Int.self)
24+
self.end = try container.decode(Int.self)
25+
guard container.isAtEnd else {
26+
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Unexpected elements at end of range"))
27+
}
28+
}
29+
30+
public func encode(to encoder: any Encoder) throws {
31+
var container = encoder.unkeyedContainer()
32+
try container.encode(start)
33+
try container.encode(end)
34+
}
35+
}

0 commit comments

Comments
 (0)