-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
PennyService.swift
168 lines (161 loc) · 6.34 KB
/
PennyService.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import DiscordBM
import DiscordLogger
import AsyncHTTPClient
import NIOCore
import SotoCore
import Shared
import Logging
import NIOPosix
struct PennyService: MainService {
func bootstrapLoggingSystem(httpClient: HTTPClient) async throws {
#if DEBUG
// Discord-logging is disabled in debug based on the logger configuration,
// so we can just use an invalid url
let webhookURL = "https://discord.com/api/webhooks/1066284436045439037/dSs4nFhjpxcOh6HWD_"
#else
let webhookURL = Constants.loggingWebhookURL
#endif
DiscordGlobalConfiguration.logManager = await DiscordLogManager(
httpClient: httpClient,
configuration: .init(
aliveNotice: .init(
address: try! .url(webhookURL),
interval: nil,
message: "I'm Alive! :)",
initialNoticeMention: .user(Constants.botDevUserId)
),
sendFullLogAsAttachment: .enabled,
mentions: [
.warning: .user(Constants.botDevUserId),
.error: .user(Constants.botDevUserId),
.critical: .user(Constants.botDevUserId)
],
extraMetadata: [.warning, .error, .critical],
disabledLogLevels: [.debug, .trace],
disabledInDebug: true
)
)
await LoggingSystem.bootstrapWithDiscordLogger(
address: try! .url(webhookURL),
level: .trace,
makeMainLogHandler: { label, metadataProvider in
StreamLogHandler.standardOutput(
label: label,
metadataProvider: metadataProvider
)
}
)
}
func makeBot(httpClient: HTTPClient) async throws -> any GatewayManager {
/// Custom caching for the `getApplicationGlobalCommands` endpoint.
let clientConfiguration = ClientConfiguration(
cachingBehavior: .custom(
apiEndpoints: [
.listApplicationCommands: .seconds(60 * 60) /// 1 hour
],
apiEndpointsDefaultTTL: .seconds(5)
)
)
return await BotGatewayManager(
eventLoopGroup: MultiThreadedEventLoopGroup.singleton,
httpClient: httpClient,
clientConfiguration: clientConfiguration,
token: Constants.botToken,
presence: .init(
activities: [.init(name: "you", type: .game)],
status: .online,
afk: false
),
intents: [
.guilds,
.guildMembers,
.guildMessages,
.messageContent,
.guildMessageReactions,
.guildModeration
]
)
}
func makeCache(bot: any GatewayManager) async throws -> DiscordCache {
await DiscordCache(
gatewayManager: bot,
intents: [.guilds, .guildMembers, .messageContent, .guildMessages],
requestAllMembers: .enabled,
messageCachingPolicy: .saveEditHistoryAndDeleted
)
}
func beforeConnectCall(
bot: any GatewayManager,
cache: DiscordCache,
httpClient: HTTPClient,
awsClient: AWSClient
) async throws -> HandlerContext {
let usersService = ServiceFactory.makeUsersService(
httpClient: httpClient,
apiBaseURL: Constants.apiBaseURL
)
let pingsService = DefaultPingsService(httpClient: httpClient)
let faqsService = DefaultFaqsService(httpClient: httpClient)
let autoFaqsService = DefaultAutoFaqsService(httpClient: httpClient)
let evolutionService = DefaultEvolutionService(httpClient: httpClient)
let soService = DefaultSOService(httpClient: httpClient)
let discordService = DiscordService(discordClient: bot.client, cache: cache)
let evolutionChecker = EvolutionChecker(
evolutionService: evolutionService,
discordService: discordService
)
let soChecker = SOChecker(
soService: soService,
discordService: discordService
)
let reactionCache = ReactionCache()
let cachesService = DefaultCachesService(
awsClient: awsClient,
context: .init(
autoFaqsService: autoFaqsService,
evolutionChecker: evolutionChecker,
soChecker: soChecker,
reactionCache: reactionCache
)
)
let services = HandlerContext.Services(
usersService: usersService,
pingsService: pingsService,
faqsService: faqsService,
autoFaqsService: autoFaqsService,
cachesService: cachesService,
discordService: discordService,
renderClient: .init(
renderer: try .forPenny(
httpClient: httpClient,
logger: Logger(label: "Penny+Leaf"),
on: httpClient.eventLoopGroup.next()
)
),
evolutionChecker: evolutionChecker,
soChecker: soChecker,
reactionCache: reactionCache
)
let context = HandlerContext(
services: services,
botStateManager: BotStateManager(services: services)
)
await CommandsManager(context: context).registerCommands()
return context
}
func afterConnectCall(context: HandlerContext) async throws {
/// Wait 5 seconds to make sure the bot is completely connected to Discord through websocket,
/// and so it can receive events already.
/// This is here until when/if DiscordBM gains better support for notifying you
/// of the first connection.
/// We could manually handle that here too, but i'd like it to be available in DiscordBM.
try await Task.sleep(for: .seconds(5))
/// Initialize `BotStateManager` after `bot.connect()` and `bot.makeEventsStream()`.
/// since it communicates through Discord and will need the Gateway connection.
await context.botStateManager.start {
/// These contain cached stuff and need to wait for `BotStateManager`.
context.services.evolutionChecker.run()
context.services.soChecker.run()
}
}
}