@@ -23,6 +23,7 @@ DEALINGS IN THE SOFTWARE.
23
23
*/
24
24
25
25
import Foundation
26
+ import Vapor
26
27
27
28
/// Represents a Discord bot.
28
29
public class Bot {
@@ -85,13 +86,15 @@ public class Bot {
85
86
var pendingApplicationCommands = [ PendingAppCommand] ( )
86
87
var pendingModals = [ String : ( Interaction ) async -> Void ] ( )
87
88
var guildsCache = [ Snowflake: Guild] ( )
88
- var msgCacheLock = NSLock ( )
89
89
var isConnected = false
90
90
var http : HTTPClient !
91
- var gw : Gateway ?
92
-
91
+ var gw : Gateway !
93
92
private var onceExecute : ( ( ) async -> Void ) ? = nil
94
93
94
+ private let msgCacheLock = NSLock ( )
95
+ private let sema = DispatchSemaphore ( value: 0 )
96
+ private let app = Vapor . Application ( )
97
+
95
98
/// Initializes the Discord bot.
96
99
/// - Parameters:
97
100
/// - token: The authentification token for the bot.
@@ -103,7 +106,8 @@ public class Bot {
103
106
self . intents = intents
104
107
self . sharding = sharding
105
108
self . cacheManager = cacheManager
106
- http = . init( bot: self , token: token, version: version)
109
+ gw = Gateway ( bot: self , elg: app. eventLoopGroup)
110
+ http = . init( bot: self , token: token, version: version, app: app)
107
111
}
108
112
109
113
func cacheGuild( _ guild: Guild ) {
@@ -141,16 +145,14 @@ public class Bot {
141
145
/// - activity: The activity. Can be set to things such as "Listening to {value}", "Watching {value}", etc. Can be `nil` for no activity.
142
146
/// - Note: Certain combinations are ignored by Discord. Some examples are setting the bots `status` to offline or setting a custom status.
143
147
public func updatePresence( status: User . Status , activity: User . ActivityType ? ) {
144
- if let gw {
145
- var d : JSON = [ " status " : status. rawValue, " afk " : false ]
146
-
147
- // Requires unix time in milliseconds
148
- d [ " since " ] = status == . idle ? Date . now. timeIntervalSince1970 * 1000 : NIL
149
- d [ " activities " ] = activity? . convert ( ) ?? [ ]
150
-
151
- let payload : JSON = [ " op " : Opcode . presenceUpdate, " d " : d]
152
- gw. sendFrame ( payload)
153
- }
148
+ var d : JSON = [ " status " : status. rawValue, " afk " : false ]
149
+
150
+ // Requires unix time in milliseconds
151
+ d [ " since " ] = status == . idle ? Date . now. timeIntervalSince1970 * 1000 : NIL
152
+ d [ " activities " ] = activity? . convert ( ) ?? [ ]
153
+
154
+ let payload : JSON = [ " op " : Opcode . presenceUpdate, " d " : d]
155
+ gw. sendFrame ( payload)
154
156
}
155
157
156
158
/// Retrieve all global application commands. If you need the commands for a specific guild, use ``Guild/applicationCommands()``.
@@ -336,19 +338,16 @@ public class Bot {
336
338
}
337
339
338
340
/// Connect to Discord.
339
- /// - Attention: This method is blocking to maintain the connection to Discord.
340
- public func connect ( ) async throws {
341
- if gw == nil {
342
- gw = Gateway ( bot : self )
343
- try await gw! . startNewSession ( )
341
+ public func connect ( ) {
342
+ if !isConnected {
343
+ try ! app . eventLoopGroup . any ( ) . makeFutureWithTask {
344
+ try ! await self . gw . startNewSession ( )
345
+ } . wait ( )
344
346
isConnected = true
345
347
if let onceExecute {
346
- await onceExecute ( )
348
+ Task { await onceExecute ( ) }
347
349
self . onceExecute = nil
348
350
}
349
- while isConnected {
350
- await sleep ( 200 )
351
- }
352
351
}
353
352
}
354
353
@@ -367,6 +366,13 @@ public class Bot {
367
366
guildsCache. removeAll ( )
368
367
}
369
368
369
+ /// Disconnect from Discord and releases the block from ``run()``.
370
+ public func close( ) {
371
+ disconnect ( )
372
+ app. shutdown ( )
373
+ sema. signal ( )
374
+ }
375
+
370
376
/// Create a guild. Your bot must be in less than 10 guilds to use this.
371
377
/// - Parameters:
372
378
/// - name: Name of the guild.
@@ -404,16 +410,12 @@ public class Bot {
404
410
}
405
411
}
406
412
407
- /// Disconnects the bot from Discord, releases the block from ``connect()`` and clears the cache.
413
+ /// Disconnect from Discord and clears the cache.
408
414
public func disconnect( ) {
409
- if let gw {
415
+ if isConnected {
410
416
try ! gw. ws. close ( code: . normalClosure) . wait ( )
411
417
gw. resetGatewayValues ( )
412
418
clearCache ( )
413
- try ! gw. settings. loop. syncShutdownGracefully ( )
414
- self . gw = nil
415
-
416
- // Must be last
417
419
isConnected = false
418
420
}
419
421
}
@@ -572,9 +574,15 @@ public class Bot {
572
574
return try await http. getWebhookWithToken ( webhookId: webhookId, webhookToken: webhookToken)
573
575
}
574
576
577
+ /// Connect to Discord and blocks the app from exiting.
578
+ public func run( ) {
579
+ connect ( )
580
+ sema. wait ( )
581
+ }
582
+
575
583
/// Block further execution until the ``EventListener/onReady(user:)`` event has been dispatched.
576
584
public func waitUntilReady( ) async {
577
- while gw? . initialState? . dispatched != true {
585
+ while gw. initialState? . dispatched != true {
578
586
await sleep ( 150 )
579
587
}
580
588
}
0 commit comments