dfx 0.50.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cache/memory.js +4 -4
- package/Cache/memory.js.map +1 -1
- package/Cache/memoryTTL.js +5 -5
- package/Cache/memoryTTL.js.map +1 -1
- package/Cache/prelude.js +4 -3
- package/Cache/prelude.js.map +1 -1
- package/Cache.d.ts.map +1 -1
- package/Cache.js +15 -7
- package/Cache.js.map +1 -1
- package/DiscordConfig.d.ts +1 -1
- package/DiscordConfig.d.ts.map +1 -1
- package/DiscordConfig.js +4 -5
- package/DiscordConfig.js.map +1 -1
- package/DiscordGateway/DiscordWS.d.ts.map +1 -1
- package/DiscordGateway/DiscordWS.js +3 -2
- package/DiscordGateway/DiscordWS.js.map +1 -1
- package/DiscordGateway/Shard/heartbeats.d.ts +1 -1
- package/DiscordGateway/Shard/heartbeats.d.ts.map +1 -1
- package/DiscordGateway/Shard/heartbeats.js +4 -1
- package/DiscordGateway/Shard/heartbeats.js.map +1 -1
- package/DiscordGateway/Shard/identify.d.ts +1 -0
- package/DiscordGateway/Shard/identify.d.ts.map +1 -1
- package/DiscordGateway/Shard/identify.js +8 -5
- package/DiscordGateway/Shard/identify.js.map +1 -1
- package/DiscordGateway/Shard/invalidSession.js +1 -1
- package/DiscordGateway/Shard/invalidSession.js.map +1 -1
- package/DiscordGateway/Shard/utils.d.ts +1 -1
- package/DiscordGateway/Shard/utils.d.ts.map +1 -1
- package/DiscordGateway/Shard/utils.js +4 -2
- package/DiscordGateway/Shard/utils.js.map +1 -1
- package/DiscordGateway/Shard.d.ts +2 -1
- package/DiscordGateway/Shard.d.ts.map +1 -1
- package/DiscordGateway/Shard.js +16 -10
- package/DiscordGateway/Shard.js.map +1 -1
- package/DiscordGateway/Sharder.d.ts.map +1 -1
- package/DiscordGateway/Sharder.js +16 -15
- package/DiscordGateway/Sharder.js.map +1 -1
- package/DiscordGateway/WS.d.ts.map +1 -1
- package/DiscordGateway/WS.js +15 -10
- package/DiscordGateway/WS.js.map +1 -1
- package/DiscordGateway.d.ts.map +1 -1
- package/DiscordGateway.js +7 -6
- package/DiscordGateway.js.map +1 -1
- package/DiscordREST/utils.d.ts +4 -3
- package/DiscordREST/utils.d.ts.map +1 -1
- package/DiscordREST/utils.js +6 -6
- package/DiscordREST/utils.js.map +1 -1
- package/DiscordREST.d.ts.map +1 -1
- package/DiscordREST.js +21 -17
- package/DiscordREST.js.map +1 -1
- package/Helpers/interactions.d.ts +10 -10
- package/Helpers/interactions.d.ts.map +1 -1
- package/Helpers/interactions.js +12 -13
- package/Helpers/interactions.js.map +1 -1
- package/Interactions/builder.js +1 -1
- package/Interactions/builder.js.map +1 -1
- package/Interactions/context.d.ts.map +1 -1
- package/Interactions/context.js +4 -1
- package/Interactions/context.js.map +1 -1
- package/Interactions/gateway.d.ts.map +1 -1
- package/Interactions/gateway.js +23 -17
- package/Interactions/gateway.js.map +1 -1
- package/Interactions/handlers.d.ts.map +1 -1
- package/Interactions/handlers.js +25 -10
- package/Interactions/handlers.js.map +1 -1
- package/Interactions/utils.js +1 -1
- package/Interactions/utils.js.map +1 -1
- package/Interactions/webhook.d.ts +7 -6
- package/Interactions/webhook.d.ts.map +1 -1
- package/Interactions/webhook.js +23 -20
- package/Interactions/webhook.js.map +1 -1
- package/Log.js +1 -1
- package/Log.js.map +1 -1
- package/RateLimit/memory.js +1 -1
- package/RateLimit/memory.js.map +1 -1
- package/RateLimit.js +9 -7
- package/RateLimit.js.map +1 -1
- package/global.d.ts +1 -1
- package/global.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/Cache/memory.ts +4 -4
- package/src/Cache/memoryTTL.ts +5 -5
- package/src/Cache/prelude.ts +6 -6
- package/src/Cache.ts +28 -18
- package/src/DiscordConfig.ts +3 -2
- package/src/DiscordGateway/DiscordWS.ts +3 -3
- package/src/DiscordGateway/Shard/heartbeats.ts +6 -1
- package/src/DiscordGateway/Shard/identify.ts +7 -5
- package/src/DiscordGateway/Shard/invalidSession.ts +1 -1
- package/src/DiscordGateway/Shard/utils.ts +7 -4
- package/src/DiscordGateway/Shard.ts +24 -19
- package/src/DiscordGateway/Sharder.ts +11 -11
- package/src/DiscordGateway/WS.ts +12 -8
- package/src/DiscordGateway.ts +4 -3
- package/src/DiscordREST/utils.ts +3 -1
- package/src/DiscordREST.ts +44 -35
- package/src/Helpers/interactions.ts +32 -34
- package/src/Interactions/builder.ts +3 -3
- package/src/Interactions/context.ts +15 -15
- package/src/Interactions/gateway.ts +15 -6
- package/src/Interactions/handlers.ts +43 -40
- package/src/Interactions/utils.ts +1 -1
- package/src/Interactions/webhook.ts +18 -15
- package/src/Log.ts +1 -1
- package/src/RateLimit.ts +6 -6
- package/src/global.ts +1 -1
- package/src/package.json +12 -12
- package/src/utils/hub.ts +6 -6
- package/utils/hub.js +6 -3
- package/utils/hub.js.map +1 -1
package/src/DiscordREST.ts
CHANGED
|
@@ -24,12 +24,12 @@ export class DiscordRESTError {
|
|
|
24
24
|
export { ResponseDecodeError } from "@effect-http/client"
|
|
25
25
|
|
|
26
26
|
const make = Do($ => {
|
|
27
|
-
const { token, rest } = $(DiscordConfig)
|
|
27
|
+
const { token, rest } = $(DiscordConfig.accessWith(identity))
|
|
28
28
|
|
|
29
|
-
const http = $(Http.HttpRequestExecutor)
|
|
30
|
-
const log = $(Log)
|
|
31
|
-
const store = $(RateLimitStore)
|
|
32
|
-
const { maybeWait } = $(RateLimiter)
|
|
29
|
+
const http = $(Http.HttpRequestExecutor.accessWith(identity))
|
|
30
|
+
const log = $(Log.accessWith(identity))
|
|
31
|
+
const store = $(RateLimitStore.accessWith(identity))
|
|
32
|
+
const { maybeWait } = $(RateLimiter.accessWith(identity))
|
|
33
33
|
|
|
34
34
|
const globalRateLimit = maybeWait(
|
|
35
35
|
"dfx.rest.global",
|
|
@@ -40,15 +40,18 @@ const make = Do($ => {
|
|
|
40
40
|
// Invalid route handling (40x)
|
|
41
41
|
const badRoutesRef = $(Ref.make(HashSet.empty<string>()))
|
|
42
42
|
const addBadRoute = (route: string) =>
|
|
43
|
-
Effect.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
Effect.all(
|
|
44
|
+
[
|
|
45
|
+
log.info("DiscordREST", "addBadRoute", route),
|
|
46
|
+
badRoutesRef.update(s => s.add(route)),
|
|
47
|
+
store.incrementCounter(
|
|
48
|
+
"dfx.rest.invalid",
|
|
49
|
+
Duration.minutes(10).toMillis,
|
|
50
|
+
10000,
|
|
51
|
+
),
|
|
52
|
+
],
|
|
53
|
+
{ discard: true, concurrency: "unbounded" },
|
|
54
|
+
)
|
|
52
55
|
const isBadRoute = (route: string) => badRoutesRef.get.map(s => s.has(route))
|
|
53
56
|
const removeBadRoute = (route: string) =>
|
|
54
57
|
badRoutesRef.update(s => s.remove(route))
|
|
@@ -57,7 +60,7 @@ const make = Do($ => {
|
|
|
57
60
|
isBadRoute(route).tap(invalid =>
|
|
58
61
|
invalid
|
|
59
62
|
? maybeWait("dfx.rest.invalid", Duration.minutes(10), 10000)
|
|
60
|
-
: Effect.unit
|
|
63
|
+
: Effect.unit,
|
|
61
64
|
).asUnit
|
|
62
65
|
|
|
63
66
|
// Request rate limiting
|
|
@@ -66,16 +69,16 @@ const make = Do($ => {
|
|
|
66
69
|
const route = routeFromConfig(path, request.method)
|
|
67
70
|
const maybeBucket = $(store.getBucketForRoute(route))
|
|
68
71
|
|
|
69
|
-
const effect = maybeBucket.match(
|
|
70
|
-
() => invalidRateLimit(route),
|
|
71
|
-
bucket =>
|
|
72
|
+
const effect = maybeBucket.match({
|
|
73
|
+
onNone: () => invalidRateLimit(route),
|
|
74
|
+
onSome: bucket =>
|
|
72
75
|
Do($ => {
|
|
73
76
|
$(invalidRateLimit(route))
|
|
74
77
|
const resetAfter = millis(bucket.resetAfter)
|
|
75
78
|
|
|
76
79
|
$(maybeWait(`dfx.rest.${bucket.key}`, resetAfter, bucket.limit))
|
|
77
80
|
}),
|
|
78
|
-
)
|
|
81
|
+
})
|
|
79
82
|
|
|
80
83
|
$(effect)
|
|
81
84
|
})
|
|
@@ -99,13 +102,13 @@ const make = Do($ => {
|
|
|
99
102
|
store.removeCounter(`dfx.rest.?.${route}`),
|
|
100
103
|
store.putBucket({
|
|
101
104
|
key: bucket,
|
|
102
|
-
resetAfter: retryAfter.
|
|
105
|
+
resetAfter: retryAfter.toMillis,
|
|
103
106
|
limit: !hasBucket && remaining > 0 ? remaining : limit,
|
|
104
107
|
}),
|
|
105
108
|
)
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
$(Effect.
|
|
111
|
+
$(Effect.all(effectsToRun, { concurrency: "unbounded", discard: true }))
|
|
109
112
|
}).ignore
|
|
110
113
|
|
|
111
114
|
const httpExecutor = http.execute.filterStatusOk
|
|
@@ -146,11 +149,14 @@ const make = Do($ => {
|
|
|
146
149
|
case 403:
|
|
147
150
|
return Do($ => {
|
|
148
151
|
$(
|
|
149
|
-
Effect.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
Effect.all(
|
|
153
|
+
[
|
|
154
|
+
log.info("DiscordREST", "403", request.url),
|
|
155
|
+
addBadRoute(routeFromConfig(request.url, request.method)),
|
|
156
|
+
updateBuckets(request, response),
|
|
157
|
+
],
|
|
158
|
+
{ concurrency: "unbounded", discard: true },
|
|
159
|
+
),
|
|
154
160
|
)
|
|
155
161
|
return $(Effect.fail(e))
|
|
156
162
|
})
|
|
@@ -158,16 +164,19 @@ const make = Do($ => {
|
|
|
158
164
|
case 429:
|
|
159
165
|
return Do($ => {
|
|
160
166
|
$(
|
|
161
|
-
Effect.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
Effect.all(
|
|
168
|
+
[
|
|
169
|
+
log.info("DiscordREST", "429", request.url),
|
|
170
|
+
addBadRoute(routeFromConfig(request.url, request.method)),
|
|
171
|
+
updateBuckets(request, response),
|
|
172
|
+
Effect.sleep(
|
|
173
|
+
retryAfter(response.headers).getOrElse(() =>
|
|
174
|
+
Duration.seconds(5),
|
|
175
|
+
),
|
|
168
176
|
),
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
],
|
|
178
|
+
{ concurrency: "unbounded", discard: true },
|
|
179
|
+
),
|
|
171
180
|
)
|
|
172
181
|
return $(executor<A>(request))
|
|
173
182
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Option as Maybe
|
|
1
|
+
import { Option as Maybe } from "@effect/data/Option"
|
|
2
2
|
import * as Arr from "@effect/data/ReadonlyArray"
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -29,14 +29,16 @@ export const findSubCommand =
|
|
|
29
29
|
/**
|
|
30
30
|
* If the sub-command exists return `true`, else `false`.
|
|
31
31
|
*/
|
|
32
|
-
export const isSubCommand =
|
|
33
|
-
|
|
32
|
+
export const isSubCommand =
|
|
33
|
+
(name: string) => (_: Discord.ApplicationCommandDatum) =>
|
|
34
|
+
findSubCommand(name)(_).isSome()
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Maybe get the options for a sub-command
|
|
37
38
|
*/
|
|
38
|
-
export const subCommandOptions =
|
|
39
|
-
|
|
39
|
+
export const subCommandOptions =
|
|
40
|
+
(name: string) => (_: Discord.ApplicationCommandDatum) =>
|
|
41
|
+
findSubCommand(name)(_).flatMapNullable(o => o.options)
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* A lens for accessing nested options in a interaction.
|
|
@@ -49,7 +51,7 @@ export const optionsWithNested = (
|
|
|
49
51
|
): Discord.ApplicationCommandInteractionDataOption[] =>
|
|
50
52
|
Maybe.fromNullable(opt.options)
|
|
51
53
|
.map(opts => [...opts, ...opts.flatMap(optsFromOption)])
|
|
52
|
-
.match(() => [], identity)
|
|
54
|
+
.match({ onNone: () => [], onSome: identity })
|
|
53
55
|
|
|
54
56
|
return Maybe.fromNullable(data.options)
|
|
55
57
|
.map(opts => [...opts, ...opts.flatMap(optsFromOption)])
|
|
@@ -70,30 +72,30 @@ export const transformOptions = (
|
|
|
70
72
|
/**
|
|
71
73
|
* Return the interaction options as a name / value map.
|
|
72
74
|
*/
|
|
73
|
-
export const optionsMap =
|
|
75
|
+
export const optionsMap = (
|
|
76
|
+
data: Pick<Discord.ApplicationCommandDatum, "options">,
|
|
77
|
+
) => transformOptions(optionsWithNested(data))
|
|
74
78
|
|
|
75
79
|
/**
|
|
76
80
|
* Try find a matching option from the interaction.
|
|
77
81
|
*/
|
|
78
|
-
export const getOption =
|
|
79
|
-
|
|
80
|
-
optionsWithNested,
|
|
81
|
-
Arr.findFirst(o => o.name === name),
|
|
82
|
-
)
|
|
82
|
+
export const getOption =
|
|
83
|
+
(name: string) => (data: Pick<Discord.ApplicationCommandDatum, "options">) =>
|
|
84
|
+
Arr.findFirst(optionsWithNested(data), o => o.name === name)
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Try find a matching option from the interaction.
|
|
86
88
|
*/
|
|
87
|
-
export const focusedOption =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
89
|
+
export const focusedOption = (
|
|
90
|
+
data: Pick<Discord.ApplicationCommandDatum, "options">,
|
|
91
|
+
) => Arr.findFirst(optionsWithNested(data), o => o.focused === true)
|
|
91
92
|
|
|
92
93
|
/**
|
|
93
94
|
* Try find a matching option value from the interaction.
|
|
94
95
|
*/
|
|
95
|
-
export const optionValue =
|
|
96
|
-
|
|
96
|
+
export const optionValue =
|
|
97
|
+
(name: string) => (data: Pick<Discord.ApplicationCommandDatum, "options">) =>
|
|
98
|
+
getOption(name)(data).flatMapNullable(o => o.value)
|
|
97
99
|
|
|
98
100
|
/**
|
|
99
101
|
* Try extract resolved data
|
|
@@ -140,9 +142,7 @@ export const resolveValues =
|
|
|
140
142
|
).flatMapNullable(a => a.values as unknown as string[]),
|
|
141
143
|
)
|
|
142
144
|
const r = $(resolved(a))
|
|
143
|
-
return
|
|
144
|
-
Product.productAll(values.map(a => Maybe.fromNullable(f(a as any, r)))),
|
|
145
|
-
)
|
|
145
|
+
return Arr.compact(values.map(a => Maybe.fromNullable(f(a as any, r))))
|
|
146
146
|
})
|
|
147
147
|
|
|
148
148
|
const extractComponents = (c: Discord.Component): Discord.Component[] => {
|
|
@@ -166,10 +166,8 @@ export const components = (
|
|
|
166
166
|
/**
|
|
167
167
|
* A lens for accessing the components in a interaction.
|
|
168
168
|
*/
|
|
169
|
-
export const componentsWithValue =
|
|
170
|
-
components,
|
|
171
|
-
Arr.filter(c => "value" in c && c.value !== undefined),
|
|
172
|
-
)
|
|
169
|
+
export const componentsWithValue = (data: Discord.ModalSubmitDatum) =>
|
|
170
|
+
Arr.filter(components(data), c => "value" in c && c.value !== undefined)
|
|
173
171
|
|
|
174
172
|
/**
|
|
175
173
|
* Return the interaction components as an id / value map.
|
|
@@ -183,24 +181,24 @@ export const transformComponents = (options: Discord.Component[]) =>
|
|
|
183
181
|
/**
|
|
184
182
|
* Return the interaction components as an id / value map.
|
|
185
183
|
*/
|
|
186
|
-
export const componentsMap =
|
|
184
|
+
export const componentsMap = (data: Discord.ModalSubmitDatum) =>
|
|
185
|
+
transformComponents(components(data))
|
|
187
186
|
|
|
188
187
|
/**
|
|
189
188
|
* Try find a matching component from the interaction.
|
|
190
189
|
*/
|
|
191
|
-
export const getComponent = (id: string) =>
|
|
192
|
-
|
|
193
|
-
components,
|
|
194
|
-
|
|
190
|
+
export const getComponent = (id: string) => (data: Discord.ModalSubmitDatum) =>
|
|
191
|
+
Arr.findFirst(
|
|
192
|
+
components(data),
|
|
193
|
+
o => (o as Discord.TextInput).custom_id === id,
|
|
195
194
|
)
|
|
196
195
|
|
|
197
196
|
/**
|
|
198
197
|
* Try find a matching component value from the interaction.
|
|
199
198
|
*/
|
|
200
|
-
export const componentValue =
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
199
|
+
export const componentValue =
|
|
200
|
+
(id: string) => (data: Discord.ModalSubmitDatum) =>
|
|
201
|
+
getComponent(id)(data).flatMapNullable(o => (o as Discord.TextInput).value)
|
|
204
202
|
|
|
205
203
|
export type InteractionResponse =
|
|
206
204
|
| {
|
|
@@ -34,7 +34,7 @@ export class InteractionBuilder<R, E, TE> {
|
|
|
34
34
|
|
|
35
35
|
concat<R1, E1, TE1>(builder: InteractionBuilder<R1, E1, TE1>) {
|
|
36
36
|
return new InteractionBuilder<R | R1, E | E1, TE | TE1>(
|
|
37
|
-
this.definitions.
|
|
37
|
+
this.definitions.appendAll(builder.definitions),
|
|
38
38
|
this.transform,
|
|
39
39
|
)
|
|
40
40
|
}
|
|
@@ -120,7 +120,7 @@ export class InteractionBuilder<R, E, TE> {
|
|
|
120
120
|
)
|
|
121
121
|
.map(c => c.command)
|
|
122
122
|
|
|
123
|
-
return DiscordREST.
|
|
123
|
+
return DiscordREST.accessWithEffect(rest =>
|
|
124
124
|
rest
|
|
125
125
|
.getCurrentBotApplicationInformation()
|
|
126
126
|
.flatMap(r => r.json)
|
|
@@ -141,7 +141,7 @@ export class InteractionBuilder<R, E, TE> {
|
|
|
141
141
|
)
|
|
142
142
|
.map(c => c.command)
|
|
143
143
|
|
|
144
|
-
return DiscordREST.
|
|
144
|
+
return DiscordREST.accessWithEffect(rest =>
|
|
145
145
|
rest.bulkOverwriteGuildApplicationCommands(
|
|
146
146
|
appId,
|
|
147
147
|
guildId,
|
|
@@ -26,7 +26,7 @@ export class ResolvedDataNotFound {
|
|
|
26
26
|
export const resolvedValues = <A>(
|
|
27
27
|
f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => A | undefined,
|
|
28
28
|
) =>
|
|
29
|
-
Interaction.
|
|
29
|
+
Interaction.accessWithEffect(ix =>
|
|
30
30
|
IxHelpers.resolveValues(f)(ix).mapError(() => new ResolvedDataNotFound(ix)),
|
|
31
31
|
)
|
|
32
32
|
|
|
@@ -34,14 +34,14 @@ export const resolved = <A>(
|
|
|
34
34
|
name: string,
|
|
35
35
|
f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => A | undefined,
|
|
36
36
|
) =>
|
|
37
|
-
Interaction.
|
|
37
|
+
Interaction.accessWithEffect(ix =>
|
|
38
38
|
IxHelpers.resolveOptionValue(
|
|
39
39
|
name,
|
|
40
40
|
f,
|
|
41
41
|
)(ix).mapError(() => new ResolvedDataNotFound(ix, name)),
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
export const focusedOptionValue = FocusedOptionContext.
|
|
44
|
+
export const focusedOptionValue = FocusedOptionContext.accessWith(
|
|
45
45
|
_ => _.focusedOption.value ?? "",
|
|
46
46
|
)
|
|
47
47
|
|
|
@@ -73,7 +73,7 @@ export const handleSubCommands = <
|
|
|
73
73
|
| SubCommandNotFound,
|
|
74
74
|
Discord.InteractionResponse
|
|
75
75
|
> =>
|
|
76
|
-
ApplicationCommand.
|
|
76
|
+
ApplicationCommand.accessWithEffect(data =>
|
|
77
77
|
Arr.findFirst(IxHelpers.allSubCommands(data), _ => !!commands[_.name])
|
|
78
78
|
.mapError(() => new SubCommandNotFound(data))
|
|
79
79
|
.flatMap(command =>
|
|
@@ -83,9 +83,9 @@ export const handleSubCommands = <
|
|
|
83
83
|
),
|
|
84
84
|
)
|
|
85
85
|
|
|
86
|
-
export const currentSubCommand = SubCommandContext.
|
|
86
|
+
export const currentSubCommand = SubCommandContext.accessWith(_ => _.command)
|
|
87
87
|
|
|
88
|
-
export const optionsMap = ApplicationCommand.
|
|
88
|
+
export const optionsMap = ApplicationCommand.accessWith(IxHelpers.optionsMap)
|
|
89
89
|
|
|
90
90
|
export class RequiredOptionNotFound {
|
|
91
91
|
readonly _tag = "RequiredOptionNotFound"
|
|
@@ -98,26 +98,26 @@ export class RequiredOptionNotFound {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export const option = (name: string) =>
|
|
101
|
-
ApplicationCommand.
|
|
101
|
+
ApplicationCommand.accessWith(IxHelpers.getOption(name))
|
|
102
102
|
|
|
103
103
|
export const optionValue = (name: string) =>
|
|
104
104
|
option(name).flatMap(_ =>
|
|
105
|
-
_.flatMapNullable(a => a.value).match(
|
|
106
|
-
() =>
|
|
107
|
-
ApplicationCommand.
|
|
105
|
+
_.flatMapNullable(a => a.value).match({
|
|
106
|
+
onNone: () =>
|
|
107
|
+
ApplicationCommand.accessWithEffect(data =>
|
|
108
108
|
Effect.fail(new RequiredOptionNotFound(data, name)),
|
|
109
109
|
),
|
|
110
|
-
Effect.succeed,
|
|
111
|
-
),
|
|
110
|
+
onSome: Effect.succeed,
|
|
111
|
+
}),
|
|
112
112
|
)
|
|
113
113
|
|
|
114
114
|
export const optionValueOptional = (name: string) =>
|
|
115
115
|
option(name).map(o => o.flatMapNullable(o => o.value))
|
|
116
116
|
|
|
117
|
-
export const modalValues = ModalSubmitData.
|
|
117
|
+
export const modalValues = ModalSubmitData.accessWith(IxHelpers.componentsMap)
|
|
118
118
|
|
|
119
119
|
export const modalValueOption = (name: string) =>
|
|
120
|
-
ModalSubmitData.
|
|
120
|
+
ModalSubmitData.accessWith(IxHelpers.componentValue(name))
|
|
121
121
|
|
|
122
122
|
export class ModalValueNotFound {
|
|
123
123
|
readonly _tag = "ModalValueNotFound"
|
|
@@ -125,7 +125,7 @@ export class ModalValueNotFound {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export const modalValue = (name: string) =>
|
|
128
|
-
ModalSubmitData.
|
|
128
|
+
ModalSubmitData.accessWithEffect(data =>
|
|
129
129
|
IxHelpers.componentValue(name)(data).mapError(
|
|
130
130
|
() => new ModalValueNotFound(data, name),
|
|
131
131
|
),
|
|
@@ -47,8 +47,8 @@ export const run =
|
|
|
47
47
|
_._tag === "GuildApplicationCommand",
|
|
48
48
|
).toReadonlyArray
|
|
49
49
|
|
|
50
|
-
const gateway = $(DiscordGateway)
|
|
51
|
-
const rest = $(DiscordREST)
|
|
50
|
+
const gateway = $(DiscordGateway.accessWith(identity))
|
|
51
|
+
const rest = $(DiscordREST.accessWith(identity))
|
|
52
52
|
|
|
53
53
|
const application = $(
|
|
54
54
|
rest.getCurrentBotApplicationInformation().flatMap(a => a.json),
|
|
@@ -69,7 +69,7 @@ export const run =
|
|
|
69
69
|
GuildApplicationCommand.map(_ => _.command) as any,
|
|
70
70
|
),
|
|
71
71
|
)
|
|
72
|
-
: Effect.never
|
|
72
|
+
: Effect.never
|
|
73
73
|
|
|
74
74
|
const handle = handlers(ix.definitions, (i, r) =>
|
|
75
75
|
rest.createInteractionResponse(i.id, i.token, r),
|
|
@@ -79,7 +79,14 @@ export const run =
|
|
|
79
79
|
postHandler(handle[i.type](i)).provideService(Interaction, i),
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
-
return $(
|
|
82
|
+
return $(
|
|
83
|
+
sync
|
|
84
|
+
? Effect.all(run, globalSync, guildSync, {
|
|
85
|
+
concurrency: "unbounded",
|
|
86
|
+
discard: true,
|
|
87
|
+
}).forever
|
|
88
|
+
: run,
|
|
89
|
+
)
|
|
83
90
|
})
|
|
84
91
|
|
|
85
92
|
const makeRegistry = Do($ => {
|
|
@@ -124,5 +131,7 @@ export interface InteractionsRegistry {
|
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
export const InteractionsRegistry = Tag<InteractionsRegistry>()
|
|
127
|
-
export const InteractionsRegistryLive =
|
|
128
|
-
|
|
134
|
+
export const InteractionsRegistryLive = Layer.effect(
|
|
135
|
+
InteractionsRegistry,
|
|
136
|
+
makeRegistry,
|
|
137
|
+
)
|
|
@@ -43,67 +43,70 @@ export const handlers = <R, E, TE, A, B>(
|
|
|
43
43
|
[Discord.InteractionType.APPLICATION_COMMAND]: i => {
|
|
44
44
|
const data = i.data as Discord.ApplicationCommandDatum
|
|
45
45
|
|
|
46
|
-
return Maybe.fromNullable(Commands[data.name]).match(
|
|
47
|
-
() => Effect.fail(new DefinitionNotFound(i)),
|
|
48
|
-
command =>
|
|
46
|
+
return Maybe.fromNullable(Commands[data.name]).match({
|
|
47
|
+
onNone: () => Effect.fail(new DefinitionNotFound(i)),
|
|
48
|
+
onSome: command =>
|
|
49
49
|
command
|
|
50
50
|
.handle(i)
|
|
51
51
|
.provideService(Ctx.ApplicationCommand, data) as Handler<R, E, B>,
|
|
52
|
-
)
|
|
52
|
+
})
|
|
53
53
|
},
|
|
54
54
|
|
|
55
55
|
[Discord.InteractionType.MODAL_SUBMIT]: i => {
|
|
56
56
|
const data = i.data as Discord.ModalSubmitDatum
|
|
57
57
|
|
|
58
|
-
return ModalSubmit.
|
|
59
|
-
_
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
match
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
return ModalSubmit.findFirst(_ => _.predicate(data.custom_id)).flatMap(
|
|
59
|
+
_ =>
|
|
60
|
+
_.match({
|
|
61
|
+
onNone: () => Effect.fail(new DefinitionNotFound(i)),
|
|
62
|
+
onSome: match =>
|
|
63
|
+
match
|
|
64
|
+
.handle(i)
|
|
65
|
+
.provideService(Ctx.ModalSubmitData, data) as Handler<R, E, B>,
|
|
66
|
+
}),
|
|
66
67
|
)
|
|
67
68
|
},
|
|
68
69
|
|
|
69
70
|
[Discord.InteractionType.MESSAGE_COMPONENT]: i => {
|
|
70
71
|
const data = i.data as Discord.MessageComponentDatum
|
|
71
72
|
|
|
72
|
-
return MessageComponent.
|
|
73
|
-
_
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
73
|
+
return MessageComponent.findFirst(_ =>
|
|
74
|
+
_.predicate(data.custom_id),
|
|
75
|
+
).flatMap(_ =>
|
|
76
|
+
_.match({
|
|
77
|
+
onNone: () => Effect.fail(new DefinitionNotFound(i)),
|
|
78
|
+
onSome: match =>
|
|
79
|
+
match
|
|
80
|
+
.handle(i)
|
|
81
|
+
.provideService(Ctx.MessageComponentData, data) as Handler<
|
|
82
|
+
R,
|
|
83
|
+
E,
|
|
84
|
+
B
|
|
85
|
+
>,
|
|
86
|
+
}),
|
|
85
87
|
)
|
|
86
88
|
},
|
|
87
89
|
|
|
88
90
|
[Discord.InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE]: i => {
|
|
89
91
|
const data = i.data as Discord.ApplicationCommandDatum
|
|
90
92
|
|
|
91
|
-
return IxHelpers.focusedOption(data).match(
|
|
92
|
-
() => Effect.fail(new DefinitionNotFound(i)),
|
|
93
|
-
focusedOption =>
|
|
94
|
-
Autocomplete.
|
|
95
|
-
_
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
match
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
return IxHelpers.focusedOption(data).match({
|
|
94
|
+
onNone: () => Effect.fail(new DefinitionNotFound(i)),
|
|
95
|
+
onSome: focusedOption =>
|
|
96
|
+
Autocomplete.findFirst(_ => _.predicate(data, focusedOption)).flatMap(
|
|
97
|
+
_ =>
|
|
98
|
+
_.match({
|
|
99
|
+
onNone: () => Effect.fail(new DefinitionNotFound(i)),
|
|
100
|
+
onSome: match =>
|
|
101
|
+
match
|
|
102
|
+
.handle(i)
|
|
103
|
+
.provideService(Ctx.ApplicationCommand, data)
|
|
104
|
+
.provideService(Ctx.FocusedOptionContext, {
|
|
105
|
+
focusedOption,
|
|
106
|
+
}) as Handler<R, E, B>,
|
|
107
|
+
}),
|
|
105
108
|
),
|
|
106
|
-
)
|
|
109
|
+
})
|
|
107
110
|
},
|
|
108
111
|
}
|
|
109
112
|
}
|
|
@@ -68,7 +68,7 @@ export const splitDefinitions = <R, E, TE, A>(
|
|
|
68
68
|
}),
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
const Commands = grouped.GlobalApplicationCommand.
|
|
71
|
+
const Commands = grouped.GlobalApplicationCommand.appendAll(
|
|
72
72
|
grouped.GuildApplicationCommand,
|
|
73
73
|
).reduce(
|
|
74
74
|
{} as Record<string, DefinitionFlattenedCommand<R, E, TE, A>>,
|
|
@@ -2,6 +2,8 @@ import * as Verify from "discord-verify"
|
|
|
2
2
|
import * as D from "./definitions.js"
|
|
3
3
|
import { DefinitionNotFound, handlers } from "./handlers.js"
|
|
4
4
|
import { InteractionBuilder, Interaction } from "./index.js"
|
|
5
|
+
import * as Option from "@effect/data/Option"
|
|
6
|
+
import * as Effect from "@effect/io/Effect"
|
|
5
7
|
|
|
6
8
|
export class BadWebhookSignature {
|
|
7
9
|
readonly _tag = "BadWebhookSignature"
|
|
@@ -16,7 +18,7 @@ const checkSignature = (
|
|
|
16
18
|
crypto: SubtleCrypto,
|
|
17
19
|
algorithm: any,
|
|
18
20
|
) =>
|
|
19
|
-
|
|
21
|
+
Option.all({
|
|
20
22
|
signature: Maybe.fromNullable(headers["x-signature-ed25519"]),
|
|
21
23
|
timestamp: Maybe.fromNullable(headers["x-signature-timestamp"]),
|
|
22
24
|
})
|
|
@@ -54,11 +56,10 @@ const makeConfig = ({
|
|
|
54
56
|
})
|
|
55
57
|
export interface WebhookConfig extends ReturnType<typeof makeConfig> {}
|
|
56
58
|
export const WebhookConfig = Tag<WebhookConfig>()
|
|
57
|
-
export const makeConfigLayer =
|
|
58
|
-
Layer.succeed(WebhookConfig,
|
|
59
|
-
)
|
|
59
|
+
export const makeConfigLayer = (opts: MakeConfigOpts) =>
|
|
60
|
+
Layer.succeed(WebhookConfig, makeConfig(opts))
|
|
60
61
|
export const makeFromConfig = (a: Config<MakeConfigOpts>) =>
|
|
61
|
-
a.config.map(makeConfig)
|
|
62
|
+
Layer.effect(WebhookConfig, a.config.map(makeConfig))
|
|
62
63
|
|
|
63
64
|
export class WebhookParseError {
|
|
64
65
|
readonly _tag = "WebhookParseError"
|
|
@@ -67,13 +68,15 @@ export class WebhookParseError {
|
|
|
67
68
|
|
|
68
69
|
const fromHeadersAndBody = (headers: Headers, body: string) =>
|
|
69
70
|
Do($ => {
|
|
70
|
-
const { publicKey, crypto, algorithm } = $(
|
|
71
|
+
const { publicKey, crypto, algorithm } = $(
|
|
72
|
+
WebhookConfig.accessWith(identity),
|
|
73
|
+
)
|
|
71
74
|
$(checkSignature(publicKey, headers, body, crypto, algorithm))
|
|
72
75
|
return $(
|
|
73
|
-
Effect.
|
|
74
|
-
() => JSON.parse(body) as Discord.Interaction,
|
|
75
|
-
reason => new WebhookParseError(reason),
|
|
76
|
-
),
|
|
76
|
+
Effect.try({
|
|
77
|
+
try: () => JSON.parse(body) as Discord.Interaction,
|
|
78
|
+
catch: reason => new WebhookParseError(reason),
|
|
79
|
+
}),
|
|
77
80
|
)
|
|
78
81
|
})
|
|
79
82
|
|
|
@@ -82,14 +85,14 @@ const run = <R, E>(
|
|
|
82
85
|
readonly [
|
|
83
86
|
handler: D.InteractionDefinition<R, E>,
|
|
84
87
|
transform: (
|
|
85
|
-
self: Effect<R, E, Discord.InteractionResponse>,
|
|
86
|
-
) => Effect<R, E, Discord.InteractionResponse>,
|
|
88
|
+
self: Effect.Effect<R, E, Discord.InteractionResponse>,
|
|
89
|
+
) => Effect.Effect<R, E, Discord.InteractionResponse>,
|
|
87
90
|
]
|
|
88
91
|
>,
|
|
89
92
|
handleResponse: (
|
|
90
93
|
ix: Discord.Interaction,
|
|
91
94
|
_: Discord.InteractionResponse,
|
|
92
|
-
) => Effect<R, E, Discord.InteractionResponse>,
|
|
95
|
+
) => Effect.Effect<R, E, Discord.InteractionResponse>,
|
|
93
96
|
) => {
|
|
94
97
|
const handler = handlers(definitions, handleResponse)
|
|
95
98
|
return (headers: Headers, body: string) =>
|
|
@@ -107,8 +110,8 @@ const run = <R, E>(
|
|
|
107
110
|
export interface HandleWebhookOpts<E> {
|
|
108
111
|
headers: Headers
|
|
109
112
|
body: string
|
|
110
|
-
success: (a: Discord.InteractionResponse) => Effect<never, never, void>
|
|
111
|
-
error: (e: Cause<E>) => Effect<never, never, void>
|
|
113
|
+
success: (a: Discord.InteractionResponse) => Effect.Effect<never, never, void>
|
|
114
|
+
error: (e: Cause<E>) => Effect.Effect<never, never, void>
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
/**
|