dfx 0.42.1 → 0.42.3

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.
Files changed (100) hide show
  1. package/Cache/driver.js.map +1 -1
  2. package/Cache/memory.js.map +1 -1
  3. package/Cache/memoryTTL.js.map +1 -1
  4. package/Cache/prelude.js.map +1 -1
  5. package/Cache.js.map +1 -1
  6. package/DiscordConfig.js.map +1 -1
  7. package/DiscordGateway/DiscordWS.js +1 -1
  8. package/DiscordGateway/DiscordWS.js.map +1 -1
  9. package/DiscordGateway/Shard/heartbeats.js.map +1 -1
  10. package/DiscordGateway/Shard/identify.js.map +1 -1
  11. package/DiscordGateway/Shard/invalidSession.js.map +1 -1
  12. package/DiscordGateway/Shard/sendEvents.js.map +1 -1
  13. package/DiscordGateway/Shard/utils.js.map +1 -1
  14. package/DiscordGateway/Shard.js.map +1 -1
  15. package/DiscordGateway/ShardStore.js.map +1 -1
  16. package/DiscordGateway/Sharder.js.map +1 -1
  17. package/DiscordGateway/WS.d.ts +3 -3
  18. package/DiscordGateway/WS.js +25 -18
  19. package/DiscordGateway/WS.js.map +1 -1
  20. package/DiscordGateway.js.map +1 -1
  21. package/DiscordREST/types.js.map +1 -1
  22. package/DiscordREST/utils.js.map +1 -1
  23. package/DiscordREST.js.map +1 -1
  24. package/Helpers/flags.js.map +1 -1
  25. package/Helpers/intents.js.map +1 -1
  26. package/Helpers/interactions.js.map +1 -1
  27. package/Helpers/members.js.map +1 -1
  28. package/Helpers/permissions.js.map +1 -1
  29. package/Helpers/ui.js.map +1 -1
  30. package/Interactions/context.js.map +1 -1
  31. package/Interactions/definitions.js.map +1 -1
  32. package/Interactions/gateway.js.map +1 -1
  33. package/Interactions/handlers.js.map +1 -1
  34. package/Interactions/index.js.map +1 -1
  35. package/Interactions/utils.js.map +1 -1
  36. package/Interactions/webhook.js.map +1 -1
  37. package/Log.js.map +1 -1
  38. package/RateLimit/memory.js.map +1 -1
  39. package/RateLimit/utils.js.map +1 -1
  40. package/RateLimit.js.map +1 -1
  41. package/_common.js.map +1 -1
  42. package/gateway.js.map +1 -1
  43. package/global.js.map +1 -1
  44. package/index.js.map +1 -1
  45. package/package.json +50 -52
  46. package/src/Cache/driver.ts +31 -0
  47. package/src/Cache/memory.ts +76 -0
  48. package/src/Cache/memoryTTL.ts +201 -0
  49. package/src/Cache/prelude.ts +215 -0
  50. package/src/Cache.ts +140 -0
  51. package/src/DiscordConfig.ts +48 -0
  52. package/src/DiscordGateway/DiscordWS.ts +74 -0
  53. package/src/DiscordGateway/Shard/heartbeats.ts +42 -0
  54. package/src/DiscordGateway/Shard/identify.ts +52 -0
  55. package/src/DiscordGateway/Shard/invalidSession.ts +10 -0
  56. package/src/DiscordGateway/Shard/sendEvents.ts +37 -0
  57. package/src/DiscordGateway/Shard/utils.ts +14 -0
  58. package/src/DiscordGateway/Shard.ts +152 -0
  59. package/src/DiscordGateway/ShardStore.ts +33 -0
  60. package/src/DiscordGateway/Sharder.ts +102 -0
  61. package/src/DiscordGateway/WS.ts +122 -0
  62. package/src/DiscordGateway.ts +43 -0
  63. package/src/DiscordREST/types.ts +13 -0
  64. package/src/DiscordREST/utils.ts +33 -0
  65. package/src/DiscordREST.ts +203 -0
  66. package/src/Helpers/flags.ts +68 -0
  67. package/src/Helpers/intents.ts +34 -0
  68. package/src/Helpers/interactions.ts +229 -0
  69. package/src/Helpers/members.ts +14 -0
  70. package/src/Helpers/permissions.ts +140 -0
  71. package/src/Helpers/ui.ts +103 -0
  72. package/src/Interactions/context.ts +132 -0
  73. package/src/Interactions/definitions.ts +309 -0
  74. package/src/Interactions/gateway.ts +71 -0
  75. package/src/Interactions/handlers.ts +130 -0
  76. package/src/Interactions/index.ts +108 -0
  77. package/src/Interactions/utils.ts +81 -0
  78. package/src/Interactions/webhook.ts +110 -0
  79. package/src/Log.ts +17 -0
  80. package/src/RateLimit/memory.ts +57 -0
  81. package/src/RateLimit/utils.ts +27 -0
  82. package/src/RateLimit.ts +69 -0
  83. package/src/_common.ts +43 -0
  84. package/src/gateway.ts +38 -0
  85. package/src/global.ts +45 -0
  86. package/src/index.ts +20 -0
  87. package/src/package.json +52 -0
  88. package/src/types.ts +6368 -0
  89. package/src/utils/effect.ts +0 -0
  90. package/src/utils/hub.ts +47 -0
  91. package/src/utils/json.d.ts +1 -0
  92. package/src/utils/tsplus.ts +10 -0
  93. package/src/webhooks.ts +41 -0
  94. package/tsconfig.json +23 -0
  95. package/tsplus.config.json +8 -0
  96. package/types.js.map +1 -1
  97. package/utils/effect.js.map +1 -1
  98. package/utils/hub.js.map +1 -1
  99. package/utils/tsplus.js.map +1 -1
  100. package/webhooks.js.map +1 -1
@@ -0,0 +1,229 @@
1
+ import { Option as Maybe, Product } from "@effect/data/Option"
2
+ import * as Arr from "@effect/data/ReadonlyArray"
3
+
4
+ /**
5
+ * Maybe find a sub-command within the interaction options.
6
+ */
7
+ export const allSubCommands = (interaction: Discord.ApplicationCommandDatum) =>
8
+ pipe(
9
+ optionsWithNested(interaction),
10
+ Arr.filter(
11
+ o => o.type === Discord.ApplicationCommandOptionType.SUB_COMMAND,
12
+ ),
13
+ )
14
+
15
+ /**
16
+ * Maybe find a sub-command within the interaction options.
17
+ */
18
+ export const findSubCommand =
19
+ (name: string) => (interaction: Discord.ApplicationCommandDatum) =>
20
+ pipe(
21
+ optionsWithNested(interaction),
22
+ Arr.findFirst(
23
+ o =>
24
+ o.type === Discord.ApplicationCommandOptionType.SUB_COMMAND &&
25
+ o.name === name,
26
+ ),
27
+ )
28
+
29
+ /**
30
+ * If the sub-command exists return `true`, else `false`.
31
+ */
32
+ export const isSubCommand = (name: string) =>
33
+ flow(findSubCommand(name), o => o.isSome())
34
+
35
+ /**
36
+ * Maybe get the options for a sub-command
37
+ */
38
+ export const subCommandOptions = (name: string) =>
39
+ flow(findSubCommand(name), o => o.flatMapNullable(o => o.options))
40
+
41
+ /**
42
+ * A lens for accessing nested options in a interaction.
43
+ */
44
+ export const optionsWithNested = (
45
+ data: Pick<Discord.ApplicationCommandDatum, "options">,
46
+ ): Discord.ApplicationCommandInteractionDataOption[] => {
47
+ const optsFromOption = (
48
+ opt: Discord.ApplicationCommandInteractionDataOption,
49
+ ): Discord.ApplicationCommandInteractionDataOption[] =>
50
+ Maybe.fromNullable(opt.options)
51
+ .map(opts => [...opts, ...opts.flatMap(optsFromOption)])
52
+ .match(() => [], identity)
53
+
54
+ return Maybe.fromNullable(data.options)
55
+ .map(opts => [...opts, ...opts.flatMap(optsFromOption)])
56
+ .getOrElse(() => [])
57
+ }
58
+
59
+ /**
60
+ * Return the interaction options as a name / value map.
61
+ */
62
+ export const transformOptions = (
63
+ options: Discord.ApplicationCommandInteractionDataOption[],
64
+ ) =>
65
+ options.reduce(
66
+ (map, option) => map.set(option.name, option.value),
67
+ HashMap.empty<string, string | undefined>(),
68
+ )
69
+
70
+ /**
71
+ * Return the interaction options as a name / value map.
72
+ */
73
+ export const optionsMap = flow(optionsWithNested, transformOptions)
74
+
75
+ /**
76
+ * Try find a matching option from the interaction.
77
+ */
78
+ export const getOption = (name: string) =>
79
+ flow(
80
+ optionsWithNested,
81
+ Arr.findFirst(o => o.name === name),
82
+ )
83
+
84
+ /**
85
+ * Try find a matching option from the interaction.
86
+ */
87
+ export const focusedOption = flow(
88
+ optionsWithNested,
89
+ Arr.findFirst(o => o.focused === true),
90
+ )
91
+
92
+ /**
93
+ * Try find a matching option value from the interaction.
94
+ */
95
+ export const optionValue = (name: string) =>
96
+ flow(getOption(name), o => o.flatMapNullable(o => o.value))
97
+
98
+ /**
99
+ * Try extract resolved data
100
+ */
101
+ export const resolved = (data: Discord.Interaction) =>
102
+ Maybe.fromNullable(data.data).flatMapNullable(
103
+ a => (a as Discord.ApplicationCommandDatum).resolved,
104
+ )
105
+
106
+ /**
107
+ * Try find a matching option value from the interaction.
108
+ */
109
+ export const resolveOptionValue =
110
+ <T>(
111
+ name: string,
112
+ f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => T | undefined,
113
+ ) =>
114
+ (a: Discord.Interaction): Maybe<T> =>
115
+ Do($ => {
116
+ const data = $(
117
+ Maybe.fromNullable(a.data as Discord.ApplicationCommandDatum),
118
+ )
119
+ const id = $(
120
+ getOption(name)(data).flatMapNullable(
121
+ ({ value }) => value as Discord.Snowflake,
122
+ ),
123
+ )
124
+ const r = $(resolved(a))
125
+ return $(Maybe.fromNullable(f(id, r)))
126
+ })
127
+
128
+ /**
129
+ * Try find matching option values from the interaction.
130
+ */
131
+ export const resolveValues =
132
+ <T>(
133
+ f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => T | undefined,
134
+ ) =>
135
+ (a: Discord.Interaction): Maybe<readonly T[]> =>
136
+ Do($ => {
137
+ const values = $(
138
+ Maybe.fromNullable(
139
+ a.data as Discord.MessageComponentDatum,
140
+ ).flatMapNullable(a => a.values as unknown as string[]),
141
+ )
142
+ const r = $(resolved(a))
143
+ return $(
144
+ Product.productAll(values.map(a => Maybe.fromNullable(f(a as any, r)))),
145
+ )
146
+ })
147
+
148
+ const extractComponents = (c: Discord.Component): Discord.Component[] => {
149
+ if ("components" in c) {
150
+ return [...c.components, ...c.components.flatMap(extractComponents)]
151
+ }
152
+
153
+ return []
154
+ }
155
+
156
+ /**
157
+ * A lens for accessing the components in a interaction.
158
+ */
159
+ export const components = (
160
+ a: Discord.ModalSubmitDatum,
161
+ ): Discord.Component[] => [
162
+ ...a.components,
163
+ ...a.components.flatMap(extractComponents),
164
+ ]
165
+
166
+ /**
167
+ * A lens for accessing the components in a interaction.
168
+ */
169
+ export const componentsWithValue = flow(
170
+ components,
171
+ Arr.filter(c => "value" in c && c.value !== undefined),
172
+ )
173
+
174
+ /**
175
+ * Return the interaction components as an id / value map.
176
+ */
177
+ export const transformComponents = (options: Discord.Component[]) =>
178
+ (options as Discord.TextInput[]).reduce(
179
+ (map, c) => (c.custom_id ? map.set(c.custom_id, c.value) : map),
180
+ HashMap.empty<string, string | undefined>(),
181
+ )
182
+
183
+ /**
184
+ * Return the interaction components as an id / value map.
185
+ */
186
+ export const componentsMap = flow(components, transformComponents)
187
+
188
+ /**
189
+ * Try find a matching component from the interaction.
190
+ */
191
+ export const getComponent = (id: string) =>
192
+ flow(
193
+ components,
194
+ Arr.findFirst(o => (o as Discord.TextInput).custom_id === id),
195
+ )
196
+
197
+ /**
198
+ * Try find a matching component value from the interaction.
199
+ */
200
+ export const componentValue = (id: string) =>
201
+ flow(getComponent(id), o =>
202
+ o.flatMapNullable(o => (o as Discord.TextInput).value),
203
+ )
204
+
205
+ export type InteractionResponse =
206
+ | {
207
+ type: Discord.InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE
208
+ data: Discord.InteractionCallbackMessage
209
+ }
210
+ | {
211
+ type: Discord.InteractionCallbackType.UPDATE_MESSAGE
212
+ data: Discord.InteractionCallbackMessage
213
+ }
214
+ | {
215
+ type: Discord.InteractionCallbackType.MODAL
216
+ data: Discord.InteractionCallbackModal
217
+ }
218
+ | {
219
+ type: Discord.InteractionCallbackType.DEFERRED_UPDATE_MESSAGE
220
+ }
221
+ | {
222
+ type: Discord.InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
223
+ }
224
+ | {
225
+ type: Discord.InteractionCallbackType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT
226
+ data: Discord.InteractionCallbackAutocomplete
227
+ }
228
+
229
+ export const response = (r: InteractionResponse) => r
@@ -0,0 +1,14 @@
1
+ /**
2
+ * From a list of roles, filter out the ones the guild member has.
3
+ */
4
+ export const roles = (roles: Discord.Role[]) => (member: Discord.GuildMember) =>
5
+ roles.filter(
6
+ role => member.roles.includes(role.id) || role.name === "@everyone",
7
+ )
8
+
9
+ /**
10
+ * Type-guard function for checking if the object is a guild member
11
+ */
12
+ export const is = (thing: unknown): thing is Discord.GuildMember =>
13
+ Object.prototype.hasOwnProperty.call(thing, "roles") &&
14
+ Object.prototype.hasOwnProperty.call(thing, "joined_at")
@@ -0,0 +1,140 @@
1
+ import * as Flags from "dfx/Helpers/flags"
2
+ import * as Members from "dfx/Helpers/members"
3
+
4
+ /**
5
+ * A constant of all the permissions
6
+ */
7
+ export const ALL = Flags.all(Discord.PermissionFlag)
8
+
9
+ /**
10
+ * Check if a flag exists in the permissions.
11
+ */
12
+ export const has = Flags.hasBigInt
13
+
14
+ /**
15
+ * Convert a permissions bitfield to a list of flag names.
16
+ */
17
+ export const toList = Flags.toList(Discord.PermissionFlag)
18
+
19
+ /**
20
+ * Convert a list of flag names to a bitfield.
21
+ */
22
+ export const fromList = Flags.fromListBigint(Discord.PermissionFlag)
23
+
24
+ /**
25
+ * Reduce a list of roles to a bitfield of all the permissions added together.
26
+ */
27
+ export const forRoles = (roles: Discord.Role[]) =>
28
+ roles.reduce(
29
+ (permissions, role) => permissions | BigInt(role.permissions),
30
+ BigInt(0),
31
+ )
32
+
33
+ /**
34
+ * From a list of roles, calculate the permissions bitfield for the member.
35
+ */
36
+ export const forMember =
37
+ (roles: Discord.Role[]) => (member: Discord.GuildMember) =>
38
+ pipe(Members.roles(roles)(member), forRoles)
39
+
40
+ const overwriteIsForMember =
41
+ (guildId?: string) =>
42
+ (member: Discord.GuildMember) =>
43
+ (overwrite: Discord.Overwrite) => {
44
+ if (overwrite.type === 0) {
45
+ return overwrite.id === guildId || member.roles.includes(overwrite.id)
46
+ }
47
+ return overwrite.id === member.user?.id
48
+ }
49
+
50
+ const overwriteIsForRole =
51
+ (guildId?: string) =>
52
+ (role: Discord.Role) =>
53
+ (overwrite: Discord.Overwrite) => {
54
+ if (overwrite.type === 0) {
55
+ return overwrite.id === guildId || overwrite.id === role.id
56
+ }
57
+
58
+ return false
59
+ }
60
+
61
+ /**
62
+ * From a list of roles and a channel, calculate the permission bitfield for
63
+ * the guild member or role for that channel.
64
+ */
65
+ export const forChannel =
66
+ (roles: Discord.Role[]) =>
67
+ ({ guild_id, permission_overwrites: overwrites = [] }: Discord.Channel) =>
68
+ (memberOrRole: Discord.GuildMember | Discord.Role) => {
69
+ const hasAdmin = has(Discord.PermissionFlag.ADMINISTRATOR)
70
+ let basePermissions: bigint
71
+ let filteredOverwrites: Discord.Overwrite[]
72
+
73
+ if (Members.is(memberOrRole)) {
74
+ if (memberOrRole.permissions) return BigInt(memberOrRole.permissions)
75
+
76
+ const memberRoles = Members.roles(roles)(memberOrRole)
77
+ basePermissions = forRoles(memberRoles)
78
+ filteredOverwrites = overwrites.filter(
79
+ overwriteIsForMember(guild_id)(memberOrRole),
80
+ )
81
+ } else {
82
+ const everyone = roles.find(role => role.name === "@everyone")
83
+
84
+ basePermissions =
85
+ BigInt(everyone?.permissions || "0") | BigInt(memberOrRole.permissions)
86
+ filteredOverwrites = overwrites.filter(
87
+ overwriteIsForRole(guild_id)(memberOrRole),
88
+ )
89
+ }
90
+
91
+ if (hasAdmin(basePermissions)) {
92
+ return ALL
93
+ }
94
+
95
+ return applyOverwrites(basePermissions)(filteredOverwrites)
96
+ }
97
+
98
+ /**
99
+ * Apply permission overwrites to a bitfield.
100
+ */
101
+ export const applyOverwrites =
102
+ (permissions: bigint) => (overwrites: Discord.Overwrite[]) =>
103
+ overwrites.reduce(
104
+ (permissions, overwrite) =>
105
+ (permissions & ~BigInt(overwrite.deny)) | BigInt(overwrite.allow),
106
+ permissions,
107
+ )
108
+
109
+ interface RolesCache<E> {
110
+ getForParent: (
111
+ parentId: string,
112
+ ) => Effect<never, E, ReadonlyMap<string, Discord.Role>>
113
+ }
114
+
115
+ export const hasInChannel =
116
+ <E>(rolesCache: RolesCache<E>, permission: bigint) =>
117
+ (
118
+ channel: Discord.Channel,
119
+ memberOrRole: Discord.GuildMember | Discord.Role,
120
+ ) =>
121
+ Do($ => {
122
+ const roles = $(rolesCache.getForParent(channel.guild_id!))
123
+ const channelPerms = forChannel([...roles.values()])(channel)(
124
+ memberOrRole,
125
+ )
126
+ return has(permission)(channelPerms)
127
+ })
128
+
129
+ export const hasInGuild =
130
+ <E>(rolesCache: RolesCache<E>, permission: bigint) =>
131
+ (guildId: Discord.Snowflake, member: Discord.GuildMember) =>
132
+ Do($ => {
133
+ const roles = $(rolesCache.getForParent(guildId))
134
+ const hasPerm = has(permission)
135
+
136
+ return member.roles.some(id => {
137
+ const role = roles.get(id)
138
+ return role ? hasPerm(role.permissions) : false
139
+ })
140
+ })
@@ -0,0 +1,103 @@
1
+ import {
2
+ ActionRow,
3
+ Button,
4
+ ButtonStyle,
5
+ Component,
6
+ ComponentType,
7
+ SelectMenu,
8
+ SelectOption,
9
+ TextInput,
10
+ TextInputStyle,
11
+ } from "../types.js"
12
+
13
+ export type UIComponent = Exclude<Component, ActionRow>
14
+
15
+ /**
16
+ * Helper to create an Action Row grid.
17
+ */
18
+ export const grid = (items: UIComponent[][]): ActionRow[] =>
19
+ items.map(
20
+ (components): ActionRow => ({
21
+ type: ComponentType.ACTION_ROW,
22
+ components,
23
+ }),
24
+ )
25
+
26
+ /**
27
+ * Helper to create a single column of components
28
+ */
29
+ export const singleColumn = (items: UIComponent[]): ActionRow[] =>
30
+ items.map(c => ({
31
+ type: ComponentType.ACTION_ROW,
32
+ components: [c],
33
+ }))
34
+
35
+ /**
36
+ * Helper to create a button component.
37
+ */
38
+ export const button = (button: Partial<Button>): Button => ({
39
+ type: ComponentType.BUTTON,
40
+ style: ButtonStyle.PRIMARY,
41
+ ...button,
42
+ })
43
+
44
+ type BasicSelect = Omit<SelectMenu, "type" | "channel_types" | "options">
45
+
46
+ type StringSelect = BasicSelect & {
47
+ options: SelectOption[]
48
+ }
49
+
50
+ type ChannelSelect = Omit<SelectMenu, "type" | "options">
51
+
52
+ /**
53
+ * Helper to create a select component.
54
+ */
55
+ export const select = (select: StringSelect): SelectMenu => ({
56
+ type: ComponentType.STRING_SELECT,
57
+ ...select,
58
+ })
59
+
60
+ /**
61
+ * Helper to create a select component.
62
+ */
63
+ export const userSelect = (select: BasicSelect): SelectMenu => ({
64
+ type: ComponentType.USER_SELECT,
65
+ ...select,
66
+ })
67
+
68
+ /**
69
+ * Helper to create a select component.
70
+ */
71
+ export const roleSelect = (select: BasicSelect): SelectMenu => ({
72
+ type: ComponentType.ROLE_SELECT,
73
+ ...select,
74
+ })
75
+
76
+ /**
77
+ * Helper to create a select component.
78
+ */
79
+ export const mentionableSelect = (select: BasicSelect): SelectMenu => ({
80
+ type: ComponentType.MENTIONABLE_SELECT,
81
+ ...select,
82
+ })
83
+
84
+ /**
85
+ * Helper to create a select component.
86
+ */
87
+ export const channelSelect = (select: ChannelSelect): SelectMenu => ({
88
+ type: ComponentType.CHANNEL_SELECT,
89
+ ...select,
90
+ })
91
+
92
+ type TextInputOpts = Omit<TextInput, "type" | "style"> & {
93
+ style?: TextInputStyle
94
+ }
95
+
96
+ /**
97
+ * Helper to create a text input
98
+ */
99
+ export const textInput = (input: TextInputOpts): TextInput => ({
100
+ type: ComponentType.TEXT_INPUT,
101
+ style: TextInputStyle.SHORT,
102
+ ...input,
103
+ })
@@ -0,0 +1,132 @@
1
+ import * as Arr from "@effect/data/ReadonlyArray"
2
+ import { Effect, EffectTypeId } from "@effect/io/Effect"
3
+ import * as IxHelpers from "dfx/Helpers/interactions"
4
+ import { ModalSubmitDatum } from "dfx/types"
5
+
6
+ export const Interaction = Tag<Discord.Interaction>()
7
+ export const ApplicationCommand = Tag<Discord.ApplicationCommandDatum>()
8
+ export const MessageComponentData = Tag<Discord.MessageComponentDatum>()
9
+ export const ModalSubmitData = Tag<Discord.ModalSubmitDatum>()
10
+
11
+ export interface FocusedOptionContext {
12
+ readonly focusedOption: Discord.ApplicationCommandInteractionDataOption
13
+ }
14
+ export const FocusedOptionContext = Tag<FocusedOptionContext>()
15
+
16
+ export interface SubCommandContext {
17
+ readonly command: Discord.ApplicationCommandInteractionDataOption
18
+ }
19
+ export const SubCommandContext = Tag<SubCommandContext>()
20
+
21
+ export class ResolvedDataNotFound {
22
+ readonly _tag = "ResolvedDataNotFound"
23
+ constructor(readonly data: Discord.Interaction, readonly name?: string) {}
24
+ }
25
+
26
+ export const resolvedValues = <A>(
27
+ f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => A | undefined,
28
+ ) =>
29
+ Interaction.flatMap(ix =>
30
+ IxHelpers.resolveValues(f)(ix).mapError(() => new ResolvedDataNotFound(ix)),
31
+ )
32
+
33
+ export const resolved = <A>(
34
+ name: string,
35
+ f: (id: Discord.Snowflake, data: Discord.ResolvedDatum) => A | undefined,
36
+ ) =>
37
+ Interaction.flatMap(ix =>
38
+ IxHelpers.resolveOptionValue(
39
+ name,
40
+ f,
41
+ )(ix).mapError(() => new ResolvedDataNotFound(ix, name)),
42
+ )
43
+
44
+ export const focusedOptionValue = FocusedOptionContext.map(
45
+ _ => _.focusedOption.value ?? "",
46
+ )
47
+
48
+ export class SubCommandNotFound {
49
+ readonly _tag = "SubCommandNotFound"
50
+ constructor(readonly data: Discord.ApplicationCommandDatum) {}
51
+ }
52
+
53
+ export const handleSubCommands = <
54
+ NER extends Record<string, Effect<any, any, Discord.InteractionResponse>>,
55
+ >(
56
+ commands: NER,
57
+ ): Effect<
58
+ | Exclude<
59
+ [NER[keyof NER]] extends [
60
+ { [EffectTypeId]: { _R: (_: never) => infer R } },
61
+ ]
62
+ ? R
63
+ : never,
64
+ SubCommandContext
65
+ >
66
+ | Discord.Interaction
67
+ | Discord.ApplicationCommandDatum,
68
+ | ([NER[keyof NER]] extends [
69
+ { [EffectTypeId]: { _E: (_: never) => infer E } },
70
+ ]
71
+ ? E
72
+ : never)
73
+ | SubCommandNotFound,
74
+ Discord.InteractionResponse
75
+ > =>
76
+ ApplicationCommand.flatMap(data =>
77
+ Arr.findFirst(IxHelpers.allSubCommands(data), _ => !!commands[_.name])
78
+ .mapError(() => new SubCommandNotFound(data))
79
+ .flatMap(command =>
80
+ commands[command.name].provideService(SubCommandContext, {
81
+ command,
82
+ }),
83
+ ),
84
+ )
85
+
86
+ export const currentSubCommand = SubCommandContext.map(_ => _.command)
87
+
88
+ export const optionsMap = ApplicationCommand.map(IxHelpers.optionsMap)
89
+
90
+ export class RequiredOptionNotFound {
91
+ readonly _tag = "RequiredOptionNotFound"
92
+ constructor(
93
+ readonly data:
94
+ | Discord.ApplicationCommandDatum
95
+ | Discord.ApplicationCommandInteractionDataOption,
96
+ readonly name: string,
97
+ ) {}
98
+ }
99
+
100
+ export const option = (name: string) =>
101
+ ApplicationCommand.map(IxHelpers.getOption(name))
102
+
103
+ export const optionValue = (name: string) =>
104
+ option(name).flatMap(_ =>
105
+ _.flatMapNullable(a => a.value).match(
106
+ () =>
107
+ ApplicationCommand.flatMap(data =>
108
+ Effect.fail(new RequiredOptionNotFound(data, name)),
109
+ ),
110
+ Effect.succeed,
111
+ ),
112
+ )
113
+
114
+ export const optionValueOptional = (name: string) =>
115
+ option(name).map(o => o.flatMapNullable(o => o.value))
116
+
117
+ export const modalValues = ModalSubmitData.map(IxHelpers.componentsMap)
118
+
119
+ export const modalValueOption = (name: string) =>
120
+ ModalSubmitData.map(IxHelpers.componentValue(name))
121
+
122
+ export class ModalValueNotFound {
123
+ readonly _tag = "ModalValueNotFound"
124
+ constructor(readonly data: ModalSubmitDatum, readonly name: string) {}
125
+ }
126
+
127
+ export const modalValue = (name: string) =>
128
+ ModalSubmitData.flatMap(data =>
129
+ IxHelpers.componentValue(name)(data).mapError(
130
+ () => new ModalValueNotFound(data, name),
131
+ ),
132
+ )