dfx 0.9.7 → 0.9.9

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 (208) hide show
  1. package/{dist/DiscordConfig → DiscordConfig}/index.d.ts +0 -1
  2. package/{dist/DiscordConfig → DiscordConfig}/index.js +0 -0
  3. package/{dist/DiscordConfig → DiscordConfig}/index.js.map +0 -0
  4. package/{dist/DiscordGateway → DiscordGateway}/DiscordWS/index.d.ts +0 -1
  5. package/{dist/DiscordGateway → DiscordGateway}/DiscordWS/index.js +0 -0
  6. package/{dist/DiscordGateway → DiscordGateway}/DiscordWS/index.js.map +0 -0
  7. package/{dist/DiscordGateway → DiscordGateway}/Shard/heartbeats.d.ts +0 -1
  8. package/{dist/DiscordGateway → DiscordGateway}/Shard/heartbeats.js +0 -0
  9. package/{dist/DiscordGateway → DiscordGateway}/Shard/heartbeats.js.map +0 -0
  10. package/{dist/DiscordGateway → DiscordGateway}/Shard/identify.d.ts +0 -1
  11. package/{dist/DiscordGateway → DiscordGateway}/Shard/identify.js +0 -0
  12. package/{dist/DiscordGateway → DiscordGateway}/Shard/identify.js.map +0 -0
  13. package/{dist/DiscordGateway → DiscordGateway}/Shard/index.d.ts +0 -1
  14. package/{dist/DiscordGateway → DiscordGateway}/Shard/index.js +0 -0
  15. package/{dist/DiscordGateway → DiscordGateway}/Shard/index.js.map +0 -0
  16. package/{dist/DiscordGateway → DiscordGateway}/Shard/invalidSession.d.ts +0 -1
  17. package/{dist/DiscordGateway → DiscordGateway}/Shard/invalidSession.js +0 -0
  18. package/{dist/DiscordGateway → DiscordGateway}/Shard/invalidSession.js.map +0 -0
  19. package/{dist/DiscordGateway → DiscordGateway}/Shard/sendEvents.d.ts +0 -1
  20. package/{dist/DiscordGateway → DiscordGateway}/Shard/sendEvents.js +0 -0
  21. package/{dist/DiscordGateway → DiscordGateway}/Shard/sendEvents.js.map +0 -0
  22. package/{dist/DiscordGateway → DiscordGateway}/Shard/utils.d.ts +0 -1
  23. package/{dist/DiscordGateway → DiscordGateway}/Shard/utils.js +0 -0
  24. package/{dist/DiscordGateway → DiscordGateway}/Shard/utils.js.map +0 -0
  25. package/{dist/DiscordGateway → DiscordGateway}/ShardStore/index.d.ts +0 -1
  26. package/{dist/DiscordGateway → DiscordGateway}/ShardStore/index.js +0 -0
  27. package/{dist/DiscordGateway → DiscordGateway}/ShardStore/index.js.map +0 -0
  28. package/{dist/DiscordGateway → DiscordGateway}/Sharder/index.d.ts +0 -1
  29. package/{dist/DiscordGateway → DiscordGateway}/Sharder/index.js +0 -0
  30. package/{dist/DiscordGateway → DiscordGateway}/Sharder/index.js.map +0 -0
  31. package/{dist/DiscordGateway → DiscordGateway}/WS/index.d.ts +0 -1
  32. package/{dist/DiscordGateway → DiscordGateway}/WS/index.js +0 -0
  33. package/{dist/DiscordGateway → DiscordGateway}/WS/index.js.map +0 -0
  34. package/{dist/DiscordGateway → DiscordGateway}/index.d.ts +0 -1
  35. package/{dist/DiscordGateway → DiscordGateway}/index.js +0 -0
  36. package/{dist/DiscordGateway → DiscordGateway}/index.js.map +0 -0
  37. package/{dist/DiscordREST → DiscordREST}/index.d.ts +0 -1
  38. package/{dist/DiscordREST → DiscordREST}/index.js +0 -0
  39. package/{dist/DiscordREST → DiscordREST}/index.js.map +0 -0
  40. package/{dist/DiscordREST → DiscordREST}/types.d.ts +0 -1
  41. package/{dist/DiscordREST → DiscordREST}/types.js +0 -0
  42. package/{dist/DiscordREST → DiscordREST}/types.js.map +0 -0
  43. package/{dist/DiscordREST → DiscordREST}/utils.d.ts +0 -1
  44. package/{dist/DiscordREST → DiscordREST}/utils.js +0 -0
  45. package/{dist/DiscordREST → DiscordREST}/utils.js.map +0 -0
  46. package/{dist/Helpers → Helpers}/flags.d.ts +0 -1
  47. package/{dist/Helpers → Helpers}/flags.js +0 -0
  48. package/{dist/Helpers → Helpers}/flags.js.map +0 -0
  49. package/{dist/Helpers → Helpers}/intents.d.ts +0 -1
  50. package/{dist/Helpers → Helpers}/intents.js +0 -0
  51. package/{dist/Helpers → Helpers}/intents.js.map +0 -0
  52. package/{dist/Helpers → Helpers}/interactions.d.ts +0 -1
  53. package/{dist/Helpers → Helpers}/interactions.js +0 -0
  54. package/{dist/Helpers → Helpers}/interactions.js.map +0 -0
  55. package/{dist/Helpers → Helpers}/members.d.ts +0 -1
  56. package/{dist/Helpers → Helpers}/members.js +0 -0
  57. package/{dist/Helpers → Helpers}/members.js.map +0 -0
  58. package/{dist/Helpers → Helpers}/permissions.d.ts +0 -1
  59. package/{dist/Helpers → Helpers}/permissions.js +0 -0
  60. package/{dist/Helpers → Helpers}/permissions.js.map +0 -0
  61. package/{dist/Helpers → Helpers}/ui.d.ts +0 -1
  62. package/{dist/Helpers → Helpers}/ui.js +0 -0
  63. package/{dist/Helpers → Helpers}/ui.js.map +0 -0
  64. package/{dist/Http → Http}/index.d.ts +0 -1
  65. package/{dist/Http → Http}/index.js +0 -0
  66. package/{dist/Http → Http}/index.js.map +0 -0
  67. package/{dist/Interactions → Interactions}/context.d.ts +0 -1
  68. package/{dist/Interactions → Interactions}/context.js +0 -0
  69. package/{dist/Interactions → Interactions}/context.js.map +0 -0
  70. package/{dist/Interactions → Interactions}/definitions.d.ts +0 -1
  71. package/{dist/Interactions → Interactions}/definitions.js +0 -0
  72. package/{dist/Interactions → Interactions}/definitions.js.map +0 -0
  73. package/{dist/Interactions → Interactions}/gateway.d.ts +0 -1
  74. package/{dist/Interactions → Interactions}/gateway.js +0 -0
  75. package/{dist/Interactions → Interactions}/gateway.js.map +0 -0
  76. package/{dist/Interactions → Interactions}/handlers.d.ts +0 -1
  77. package/{dist/Interactions → Interactions}/handlers.js +0 -0
  78. package/{dist/Interactions → Interactions}/handlers.js.map +0 -0
  79. package/{dist/Interactions → Interactions}/index.d.ts +0 -1
  80. package/{dist/Interactions → Interactions}/index.js +0 -0
  81. package/{dist/Interactions → Interactions}/index.js.map +0 -0
  82. package/{dist/Interactions → Interactions}/utils.d.ts +0 -1
  83. package/{dist/Interactions → Interactions}/utils.js +0 -0
  84. package/{dist/Interactions → Interactions}/utils.js.map +0 -0
  85. package/{dist/Interactions → Interactions}/webhook.d.ts +0 -1
  86. package/{dist/Interactions → Interactions}/webhook.js +0 -0
  87. package/{dist/Interactions → Interactions}/webhook.js.map +0 -0
  88. package/{dist/Log → Log}/index.d.ts +0 -1
  89. package/{dist/Log → Log}/index.js +0 -0
  90. package/{dist/Log → Log}/index.js.map +0 -0
  91. package/{dist/RateLimitStore → RateLimitStore}/index.d.ts +0 -1
  92. package/{dist/RateLimitStore → RateLimitStore}/index.js +0 -0
  93. package/{dist/RateLimitStore → RateLimitStore}/index.js.map +0 -0
  94. package/{dist/RateLimitStore → RateLimitStore}/memory.d.ts +0 -1
  95. package/{dist/RateLimitStore → RateLimitStore}/memory.js +0 -0
  96. package/{dist/RateLimitStore → RateLimitStore}/memory.js.map +0 -0
  97. package/{dist/RateLimitStore → RateLimitStore}/utils.d.ts +0 -1
  98. package/{dist/RateLimitStore → RateLimitStore}/utils.js +0 -0
  99. package/{dist/RateLimitStore → RateLimitStore}/utils.js.map +0 -0
  100. package/{dist/common-gateway.d.ts → common-gateway.d.ts} +0 -1
  101. package/{dist/common-gateway.js → common-gateway.js} +0 -0
  102. package/{dist/common-gateway.js.map → common-gateway.js.map} +0 -0
  103. package/{dist/common.d.ts → common.d.ts} +0 -1
  104. package/{dist/common.js → common.js} +0 -0
  105. package/{dist/common.js.map → common.js.map} +0 -0
  106. package/{dist/global.d.ts → global.d.ts} +0 -1
  107. package/{dist/global.js → global.js} +0 -0
  108. package/{dist/global.js.map → global.js.map} +0 -0
  109. package/{dist/index.d.ts → index.d.ts} +0 -1
  110. package/{dist/index.js → index.js} +0 -0
  111. package/{dist/index.js.map → index.js.map} +0 -0
  112. package/package.json +7 -10
  113. package/{dist/types.d.ts → types.d.ts} +0 -1
  114. package/{dist/types.js → types.js} +0 -0
  115. package/{dist/types.js.map → types.js.map} +0 -0
  116. package/{dist/utils → utils}/effect.d.ts +0 -1
  117. package/{dist/utils → utils}/effect.js +0 -0
  118. package/{dist/utils → utils}/effect.js.map +0 -0
  119. package/{dist/utils → utils}/tsplus.d.ts +0 -1
  120. package/{dist/utils → utils}/tsplus.js +0 -0
  121. package/{dist/utils → utils}/tsplus.js.map +0 -0
  122. package/{dist/webhooks.d.ts → webhooks.d.ts} +0 -1
  123. package/{dist/webhooks.js → webhooks.js} +0 -0
  124. package/{dist/webhooks.js.map → webhooks.js.map} +0 -0
  125. package/README.md +0 -41
  126. package/dist/DiscordConfig/index.d.ts.map +0 -1
  127. package/dist/DiscordGateway/DiscordWS/index.d.ts.map +0 -1
  128. package/dist/DiscordGateway/Shard/heartbeats.d.ts.map +0 -1
  129. package/dist/DiscordGateway/Shard/identify.d.ts.map +0 -1
  130. package/dist/DiscordGateway/Shard/index.d.ts.map +0 -1
  131. package/dist/DiscordGateway/Shard/invalidSession.d.ts.map +0 -1
  132. package/dist/DiscordGateway/Shard/sendEvents.d.ts.map +0 -1
  133. package/dist/DiscordGateway/Shard/utils.d.ts.map +0 -1
  134. package/dist/DiscordGateway/ShardStore/index.d.ts.map +0 -1
  135. package/dist/DiscordGateway/Sharder/index.d.ts.map +0 -1
  136. package/dist/DiscordGateway/WS/index.d.ts.map +0 -1
  137. package/dist/DiscordGateway/index.d.ts.map +0 -1
  138. package/dist/DiscordREST/index.d.ts.map +0 -1
  139. package/dist/DiscordREST/types.d.ts.map +0 -1
  140. package/dist/DiscordREST/utils.d.ts.map +0 -1
  141. package/dist/Helpers/flags.d.ts.map +0 -1
  142. package/dist/Helpers/intents.d.ts.map +0 -1
  143. package/dist/Helpers/interactions.d.ts.map +0 -1
  144. package/dist/Helpers/members.d.ts.map +0 -1
  145. package/dist/Helpers/permissions.d.ts.map +0 -1
  146. package/dist/Helpers/ui.d.ts.map +0 -1
  147. package/dist/Http/index.d.ts.map +0 -1
  148. package/dist/Interactions/context.d.ts.map +0 -1
  149. package/dist/Interactions/definitions.d.ts.map +0 -1
  150. package/dist/Interactions/gateway.d.ts.map +0 -1
  151. package/dist/Interactions/handlers.d.ts.map +0 -1
  152. package/dist/Interactions/index.d.ts.map +0 -1
  153. package/dist/Interactions/utils.d.ts.map +0 -1
  154. package/dist/Interactions/webhook.d.ts.map +0 -1
  155. package/dist/Log/index.d.ts.map +0 -1
  156. package/dist/RateLimitStore/index.d.ts.map +0 -1
  157. package/dist/RateLimitStore/memory.d.ts.map +0 -1
  158. package/dist/RateLimitStore/utils.d.ts.map +0 -1
  159. package/dist/common-gateway.d.ts.map +0 -1
  160. package/dist/common.d.ts.map +0 -1
  161. package/dist/global.d.ts.map +0 -1
  162. package/dist/index.d.ts.map +0 -1
  163. package/dist/types.d.ts.map +0 -1
  164. package/dist/utils/effect.d.ts.map +0 -1
  165. package/dist/utils/tsplus.d.ts.map +0 -1
  166. package/dist/webhooks.d.ts.map +0 -1
  167. package/src/DiscordConfig/index.ts +0 -52
  168. package/src/DiscordGateway/DiscordWS/index.ts +0 -50
  169. package/src/DiscordGateway/Shard/heartbeats.ts +0 -39
  170. package/src/DiscordGateway/Shard/identify.ts +0 -61
  171. package/src/DiscordGateway/Shard/index.ts +0 -90
  172. package/src/DiscordGateway/Shard/invalidSession.ts +0 -11
  173. package/src/DiscordGateway/Shard/sendEvents.ts +0 -35
  174. package/src/DiscordGateway/Shard/utils.ts +0 -14
  175. package/src/DiscordGateway/ShardStore/index.ts +0 -33
  176. package/src/DiscordGateway/Sharder/index.ts +0 -95
  177. package/src/DiscordGateway/WS/index.ts +0 -99
  178. package/src/DiscordGateway/index.ts +0 -55
  179. package/src/DiscordREST/index.ts +0 -132
  180. package/src/DiscordREST/types.ts +0 -14
  181. package/src/DiscordREST/utils.ts +0 -35
  182. package/src/Helpers/flags.ts +0 -68
  183. package/src/Helpers/intents.ts +0 -32
  184. package/src/Helpers/interactions.ts +0 -180
  185. package/src/Helpers/members.ts +0 -14
  186. package/src/Helpers/permissions.ts +0 -102
  187. package/src/Helpers/ui.ts +0 -103
  188. package/src/Http/index.ts +0 -65
  189. package/src/Interactions/context.ts +0 -100
  190. package/src/Interactions/definitions.ts +0 -144
  191. package/src/Interactions/gateway.ts +0 -55
  192. package/src/Interactions/handlers.ts +0 -139
  193. package/src/Interactions/index.ts +0 -83
  194. package/src/Interactions/utils.ts +0 -81
  195. package/src/Interactions/webhook.ts +0 -96
  196. package/src/Log/index.ts +0 -23
  197. package/src/RateLimitStore/index.ts +0 -65
  198. package/src/RateLimitStore/memory.ts +0 -48
  199. package/src/RateLimitStore/utils.ts +0 -27
  200. package/src/common-gateway.ts +0 -5
  201. package/src/common.ts +0 -30
  202. package/src/global.ts +0 -50
  203. package/src/index.ts +0 -36
  204. package/src/json.d.ts +0 -1
  205. package/src/types.ts +0 -6095
  206. package/src/utils/effect.ts +0 -7
  207. package/src/utils/tsplus.ts +0 -11
  208. package/src/webhooks.ts +0 -30
@@ -1,90 +0,0 @@
1
- import * as Heartbeats from "./heartbeats.js"
2
- import * as Identify from "./identify.js"
3
- import * as InvalidSession from "./invalidSession.js"
4
- import * as Utils from "./utils.js"
5
-
6
- export const make = (shard: [id: number, count: number]) =>
7
- Do(($) => {
8
- const { token, gateway } = $(Effect.service(Config.DiscordConfig))
9
-
10
- const socket = $(DWS.make())
11
-
12
- const [emit, outgoing] = EffectSource.asyncEmitter<never, DWS.Message>()
13
- const limiter = $(Effect.service(RateLimitStore.RateLimiter))
14
- const sendEffect = outgoing
15
- .tap(() => limiter.maybeWait("shard.send", Duration.minutes(1), 120))
16
- .run(socket.sink)
17
-
18
- const raw = $(socket.source.share)
19
- const sendMessage = (a: DWS.Message) =>
20
- Effect.sync(() => {
21
- emit.data(a)
22
- })
23
-
24
- const [latestReady, updateLatestReady] = $(
25
- Utils.latest((p) =>
26
- Maybe.some(p)
27
- .filter(
28
- (p): p is Discord.GatewayPayload<Discord.ReadyEvent> =>
29
- p.op === Discord.GatewayOpcode.DISPATCH && p.t === "READY",
30
- )
31
- .map((p) => p.d!),
32
- ),
33
- )
34
- const [latestSequence, updateLatestSequence] = $(
35
- Utils.latest((p) => Maybe.fromNullable(p.s)),
36
- )
37
- const maybeUpdateUrl = (p: Discord.GatewayPayload) =>
38
- Maybe.some(p)
39
- .filter(
40
- (p): p is Discord.GatewayPayload<Discord.ReadyEvent> =>
41
- p.op === Discord.GatewayOpcode.DISPATCH && p.t === "READY",
42
- )
43
- .map((p) => p.d!)
44
- .match(
45
- () => Effect.unit(),
46
- (a) => socket.setUrl(a.resume_gateway_url),
47
- )
48
-
49
- const updateRefs = raw
50
- .tap(updateLatestReady)
51
- .tap(updateLatestSequence)
52
- .tap(maybeUpdateUrl).runDrain
53
-
54
- // heartbeats
55
- const heartbeatEffects = Heartbeats.fromRaw(raw, latestSequence).forEach(
56
- sendMessage,
57
- )
58
-
59
- const dispatch = raw.filter(
60
- (p): p is Discord.GatewayPayload<Discord.ReceiveEvent> =>
61
- p.op === Discord.GatewayOpcode.DISPATCH,
62
- )
63
-
64
- // identify
65
- const identifyEffects = Identify.fromRaw(raw, {
66
- token,
67
- shard,
68
- intents: gateway.intents,
69
- presence: gateway.presence,
70
- latestSequence,
71
- latestReady,
72
- }).forEach(sendMessage)
73
-
74
- // invalid session
75
- const invalidEffects = InvalidSession.fromRaw(raw, latestReady).forEach(
76
- sendMessage,
77
- )
78
-
79
- return {
80
- run: updateRefs
81
- .zipPar(heartbeatEffects)
82
- .zipPar(identifyEffects)
83
- .zipPar(invalidEffects)
84
- .zipPar(sendEffect),
85
- raw,
86
- dispatch,
87
- send: (p: Discord.GatewayPayload) => emit.data(p),
88
- reconnect: () => emit.data(WS.Reconnect),
89
- }
90
- })
@@ -1,11 +0,0 @@
1
- import { opCode } from "./utils.js"
2
-
3
- export const fromRaw = <R, E>(
4
- raw: EffectSource<R, E, Discord.GatewayPayload>,
5
- latestReady: Ref<Maybe<Discord.ReadyEvent>>,
6
- ) =>
7
- opCode(raw)<Discord.InvalidSessionEvent>(
8
- Discord.GatewayOpcode.INVALID_SESSION,
9
- )
10
- .tap((p) => (p.d ? Effect.unit() : latestReady.set(Maybe.none)))
11
- .map((): DWS.Message => WS.Reconnect)
@@ -1,35 +0,0 @@
1
- export const heartbeat = (d: Discord.Heartbeat): Discord.GatewayPayload => ({
2
- op: Discord.GatewayOpcode.HEARTBEAT,
3
- d,
4
- })
5
-
6
- export const identify = (d: Discord.Identify): Discord.GatewayPayload => ({
7
- op: Discord.GatewayOpcode.IDENTIFY,
8
- d,
9
- })
10
-
11
- export const resume = (d: Discord.Resume): Discord.GatewayPayload => ({
12
- op: Discord.GatewayOpcode.RESUME,
13
- d,
14
- })
15
-
16
- export const requestGuildMembers = (
17
- d: Discord.RequestGuildMember,
18
- ): Discord.GatewayPayload => ({
19
- op: Discord.GatewayOpcode.REQUEST_GUILD_MEMBERS,
20
- d,
21
- })
22
-
23
- export const voiceStateUpdate = (
24
- d: Discord.UpdateVoiceState,
25
- ): Discord.GatewayPayload => ({
26
- op: Discord.GatewayOpcode.VOICE_STATE_UPDATE,
27
- d,
28
- })
29
-
30
- export const presenceUpdate = (
31
- d: Discord.UpdatePresence,
32
- ): Discord.GatewayPayload => ({
33
- op: Discord.GatewayOpcode.PRESENCE_UPDATE,
34
- d,
35
- })
@@ -1,14 +0,0 @@
1
- export const opCode =
2
- <R, E>(source: EffectSource<R, E, Discord.GatewayPayload>) =>
3
- <T = any>(code: Discord.GatewayOpcode) =>
4
- source.filter((p): p is Discord.GatewayPayload<T> => p.op === code)
5
-
6
- const maybeUpdateRef = <T>(
7
- f: (p: Discord.GatewayPayload) => Maybe<T>,
8
- ref: Ref<Maybe<T>>,
9
- ) => flow(f, (o) => o.match(Effect.unit, (a) => ref.set(Maybe.some(a))))
10
-
11
- export const latest = <T>(f: (p: Discord.GatewayPayload) => Maybe<T>) =>
12
- Ref.make<Maybe<T>>(Maybe.none).map(
13
- (ref) => [ref, maybeUpdateRef(f, ref)] as const,
14
- )
@@ -1,33 +0,0 @@
1
- export interface ClaimIdContext {
2
- sharderCount: number
3
- totalCount: number
4
- }
5
-
6
- export interface ShardStore {
7
- claimId: (ctx: ClaimIdContext) => Effect<never, never, Maybe<number>>
8
- allClaimed: (totalCount: number) => Effect<never, never, boolean>
9
- heartbeat?: (shardId: number) => Effect<never, never, void>
10
- }
11
- export const ShardStore = Tag<ShardStore>()
12
-
13
- // Very basic shard id store, that does no health checks
14
- const memoryStore = (): ShardStore => {
15
- let currentId = 0
16
-
17
- return {
18
- claimId: ({ totalCount }) =>
19
- Effect.sync(() => {
20
- if (currentId >= totalCount) {
21
- return Maybe.none
22
- }
23
-
24
- const id = currentId
25
- currentId++
26
- return Maybe.some(id)
27
- }),
28
-
29
- allClaimed: (totalCount) => Effect.sync(() => currentId >= totalCount),
30
- }
31
- }
32
-
33
- export const LiveMemoryShardStore = Layer.sync(ShardStore)(memoryStore)
@@ -1,95 +0,0 @@
1
- import { millis } from "@fp-ts/data/Duration"
2
- import { overridePull } from "callbag-effect-ts/Source"
3
- import { ShardStore } from "../ShardStore/index.js"
4
-
5
- const configs = (totalCount: number) =>
6
- Do(($) => {
7
- const store = $(Effect.service(ShardStore))
8
- const claimId = (sharderCount: number) =>
9
- store
10
- .claimId({
11
- totalCount,
12
- sharderCount,
13
- })
14
- .flatMap(
15
- (
16
- a,
17
- ): Effect<
18
- never,
19
- never,
20
- readonly [Maybe<number>, EffectSource<never, never, number>]
21
- > =>
22
- a.match(
23
- () =>
24
- Effect.succeed([
25
- Maybe.some(sharderCount),
26
- EffectSource.empty,
27
- ] as const).delay(Duration.minutes(3)),
28
- (id) =>
29
- Effect.succeed([
30
- Maybe.some(sharderCount + 1),
31
- EffectSource.of(id),
32
- ]),
33
- ),
34
- )
35
-
36
- return EffectSource.resource(0, (sharderCount) =>
37
- EffectSource.fromEffect(claimId(sharderCount)),
38
- ).map((id) => ({
39
- id,
40
- totalCount,
41
- }))
42
- })
43
-
44
- const spawnEffect = Effect.structPar({
45
- gateway: Rest.rest
46
- .getGatewayBot()
47
- .flatMap((r) => r.json)
48
- .catchAll(() =>
49
- Effect.succeed<Discord.GetGatewayBotResponse>({
50
- url: "wss://gateway.discord.gg/",
51
- shards: 1,
52
- session_start_limit: {
53
- total: 0,
54
- remaining: 0,
55
- reset_after: 0,
56
- max_concurrency: 1,
57
- },
58
- }),
59
- ),
60
- config: Config.gateway,
61
- limiter: Effect.service(RateLimitStore.RateLimiter),
62
- })
63
- .bind("configs", ({ gateway, config }) =>
64
- configs(config.shardCount ?? gateway.shards),
65
- )
66
- .map(({ gateway, config, configs, limiter }) => {
67
- const [source, pull] = overridePull(
68
- configs,
69
- gateway.session_start_limit.max_concurrency,
70
- )
71
-
72
- return source
73
- .map((config) => ({
74
- ...config,
75
- url: gateway.url,
76
- concurrency: gateway.session_start_limit.max_concurrency,
77
- }))
78
- .groupBy((c) => c.id % c.concurrency)
79
- .chainPar(([shardConfig, key]) =>
80
- shardConfig
81
- .tap(() =>
82
- limiter.maybeWait(
83
- `gateway.sharder.${key}`,
84
- millis(config.identifyRateLimit[0]),
85
- config.identifyRateLimit[1],
86
- ),
87
- )
88
- .mapEffect((c) => Shard.make([c.id, c.totalCount])),
89
- )
90
- .tap(() => Effect.sync(pull))
91
- })
92
-
93
- export const spawn = spawnEffect.unwrap.chainPar((shard) =>
94
- EffectSource.of(shard).merge(EffectSource.fromEffect(shard.run).drain),
95
- )
@@ -1,99 +0,0 @@
1
- import WebSocket from "isomorphic-ws"
2
-
3
- export const Reconnect = Symbol()
4
- export type Reconnect = typeof Reconnect
5
- export type Message = string | Buffer | ArrayBuffer | Reconnect
6
-
7
- const socket = (urlRef: Ref<string>, options?: WebSocket.ClientOptions) =>
8
- Do(($) => {
9
- const url = $(urlRef.get)
10
- return new WebSocket(url, options)
11
- }).acquireRelease((ws) =>
12
- Effect.sync(() => {
13
- ws.close()
14
- ws.removeAllListeners()
15
- }),
16
- )
17
-
18
- export class WebSocketError {
19
- readonly _tag = "WebSocketError"
20
- constructor(readonly reason: unknown) {}
21
- }
22
-
23
- export class WebSocketCloseError {
24
- readonly _tag = "WebSocketCloseError"
25
- constructor(readonly code: number, readonly reason: string) {}
26
- }
27
-
28
- const recv = (ws: WebSocket) =>
29
- EffectSource.async<WebSocketError | WebSocketCloseError, WebSocket.RawData>(
30
- (emit) => {
31
- ws.on("message", (message) => {
32
- emit.data(message)
33
- })
34
-
35
- ws.on("error", (cause) => {
36
- emit.fail(new WebSocketError(cause))
37
- })
38
-
39
- ws.on("close", (code, reason) => {
40
- emit.fail(new WebSocketCloseError(code, reason.toString("utf8")))
41
- })
42
- },
43
- )
44
-
45
- export class WebSocketWriteError {
46
- readonly _tag = "WebSocketWriteError"
47
- constructor(readonly reason: Error) {}
48
- }
49
-
50
- const send = (ws: WebSocket, out: EffectSource<never, never, Message>) =>
51
- Do(($) => {
52
- const log = $(Effect.service(Log.Log))
53
- return Effect.async<never, never, void>((resume) => {
54
- if (ws.readyState & ws.OPEN) {
55
- resume(Effect.unit())
56
- } else {
57
- ws.once("open", () => {
58
- resume(Effect.unit())
59
- })
60
- }
61
- })
62
- .map(() => out)
63
- .unwrap.tap((p) => log.debug("WS", "send", p))
64
- .tap((data) =>
65
- Effect.async<never, WebSocketWriteError, void>((resume) => {
66
- if (data === Reconnect) {
67
- ws.close(1012, "reconnecting")
68
- resume(Effect.unit())
69
- } else {
70
- ws.send(data, (err) => {
71
- resume(
72
- err
73
- ? Effect.fail(new WebSocketWriteError(err!))
74
- : Effect.unit(),
75
- )
76
- })
77
- }
78
- }),
79
- ).drain
80
- })
81
-
82
- export const make = (url: Ref<string>, options?: WebSocket.ClientOptions) =>
83
- Do(($) => {
84
- const [sink, outbound] = EffectSource.asyncSink<never, Message>()
85
- const log = $(Effect.service(Log.Log))
86
- const withLog = Effect.provideService(Log.Log)(log)
87
-
88
- const source = Do(($) => {
89
- const ws = $(socket(url, options))
90
- const sendEffect = $(withLog(send(ws, outbound)))
91
- return recv(ws).merge(sendEffect)
92
- }).unwrapScope.retry(
93
- Schedule.recurWhile(
94
- (e) => e._tag === "WebSocketCloseError" && e.code === 1012,
95
- ),
96
- )
97
-
98
- return { source, sink }
99
- })
@@ -1,55 +0,0 @@
1
- import { spawn } from "./Sharder/index.js"
2
-
3
- const fromDispatchFactory =
4
- <R, E>(
5
- source: EffectSource<R, E, Discord.GatewayPayload<Discord.ReceiveEvent>>,
6
- ) =>
7
- <K extends keyof Discord.ReceiveEvents>(
8
- event: K,
9
- ): EffectSource<R, E, Discord.ReceiveEvents[K]> =>
10
- source.filter((p) => p.t === event).map((p) => p.d! as any)
11
-
12
- const handleDispatchFactory =
13
- <R, E>(
14
- source: EffectSource<R, E, Discord.GatewayPayload<Discord.ReceiveEvent>>,
15
- ) =>
16
- <K extends keyof Discord.ReceiveEvents, R1, E1, A>(
17
- event: K,
18
- handle: (event: Discord.ReceiveEvents[K]) => Effect<R1, E1, A>,
19
- ): Effect<R | R1, E | E1, void> =>
20
- source
21
- .filter((p) => p.t === event)
22
- .chainPar((a) => EffectSource.fromEffect(handle(a.d as any))).runDrain
23
-
24
- export const make = Do(($) => {
25
- const shards = $(spawn.share)
26
- const raw = $(shards.chainPar((s) => s.raw).share)
27
- const dispatch = $(shards.chainPar((s) => s.dispatch).share)
28
- const fromDispatch = fromDispatchFactory(dispatch)
29
- const handleDispatch = handleDispatchFactory(dispatch)
30
-
31
- return {
32
- shards,
33
- raw,
34
- dispatch,
35
- fromDispatch,
36
- handleDispatch,
37
- }
38
- })
39
-
40
- export interface DiscordGateway extends Success<typeof make> {}
41
- export const DiscordGateway = Tag<DiscordGateway>()
42
- export const LiveDiscordGateway = Layer.fromEffect(DiscordGateway)(make)
43
-
44
- export const handleDispatch = <
45
- K extends keyof Discord.ReceiveEvents,
46
- R1,
47
- E1,
48
- A,
49
- >(
50
- event: K,
51
- handle: (event: Discord.ReceiveEvents[K]) => Effect<R1, E1, A>,
52
- ) =>
53
- Effect.serviceWithEffect(DiscordGateway)((a) =>
54
- a.handleDispatch(event, handle),
55
- )
@@ -1,132 +0,0 @@
1
- import { millis } from "@fp-ts/data/Duration"
2
- import { BucketDetails } from "dfx/RateLimitStore/index"
3
- import { ResponseWithData } from "./types.js"
4
- import { rateLimitFromHeaders, routeFromConfig } from "./utils.js"
5
- import Pkg from "../../package.json" assert { type: "json" }
6
-
7
- const make = Do(($) => {
8
- const { token, rest } = $(Effect.service(Config.DiscordConfig))
9
-
10
- const log = $(Effect.service(Log.Log))
11
- const store = $(Effect.service(RateLimitStore.RateLimitStore))
12
- const { maybeWait } = $(Effect.service(RateLimitStore.RateLimiter))
13
-
14
- const globalRateLimit = maybeWait(
15
- "rest.global",
16
- rest.globalRateLimit.window,
17
- rest.globalRateLimit.limit,
18
- )
19
-
20
- const requestRateLimit = (path: string, init: RequestInit) =>
21
- Do(($) => {
22
- const route = routeFromConfig(path, init)
23
- const maybeBucket = $(store.getBucketForRoute(route))
24
- const bucket = maybeBucket.getOrElse(
25
- (): BucketDetails => ({
26
- key: `?.${Equal.hash(route)}`,
27
- resetAfter: 5000,
28
- limit: 1,
29
- }),
30
- )
31
- const resetAfter = millis(bucket.resetAfter)
32
- $(maybeWait(`rest.bucket.${bucket.key}`, resetAfter, bucket.limit))
33
- })
34
-
35
- const updateBuckets = (path: string, init: RequestInit, response: Response) =>
36
- Do(($) => {
37
- const route = routeFromConfig(path, init)
38
- const { bucket, retryAfter, limit, remaining } = $(
39
- Effect.fromOption(rateLimitFromHeaders(response.headers)),
40
- )
41
-
42
- const effectsToRun = [store.putBucketRoute(route, bucket)]
43
-
44
- const hasBucket = $(store.hasBucket(bucket))
45
- if (!hasBucket || limit - 1 === remaining) {
46
- effectsToRun.push(
47
- store.putBucket({
48
- key: bucket,
49
- resetAfter: retryAfter.millis,
50
- limit: !hasBucket && remaining > 0 ? remaining : limit,
51
- }),
52
- )
53
- }
54
-
55
- $(effectsToRun.collectAllParDiscard)
56
- }).ignore
57
-
58
- const request = <A = unknown>(
59
- path: string,
60
- init: RequestInit = {},
61
- ): Effect<
62
- never,
63
- Http.FetchError | Http.StatusCodeError | Http.JsonParseError,
64
- ResponseWithData<A>
65
- > =>
66
- requestRateLimit(path, init)
67
- .tap(() => globalRateLimit)
68
- .flatMap(() =>
69
- Http.requestWithJson<A>(`${rest.baseUrl}${path}`, {
70
- ...init,
71
- headers: {
72
- ...(init?.headers ?? {}),
73
- Authorization: `Bot ${token}`,
74
- "User-Agent": `DiscordBot (https://github.com/tim-smart/dfx, ${Pkg.version})`,
75
- },
76
- }),
77
- )
78
- .catchTag("StatusCodeError", (e) =>
79
- e.code === 429
80
- ? Do(($) => {
81
- $(log.debug("DiscordREST", "429", path))
82
- $(updateBuckets(path, init, e.response))
83
- return $(request<A>(path, init))
84
- })
85
- : Effect.fail(e),
86
- )
87
- .tap(({ response }) => updateBuckets(path, init, response))
88
-
89
- return { request }
90
- })
91
-
92
- export interface DiscordREST extends Success<typeof make> {}
93
- export const DiscordREST = Tag<DiscordREST>()
94
- export const LiveDiscordREST = Layer.fromEffect(DiscordREST)(make)
95
-
96
- export const rest = Discord.createRoutes<RequestInit>(
97
- ({ method, url, params, options = {} }) =>
98
- Effect.serviceWithEffect(DiscordREST)(({ request }) => {
99
- const hasBody = method !== "GET" && method !== "DELETE"
100
- let hasFormData = typeof (options?.body as any)?.append === "function"
101
- let body: BodyInit | undefined = undefined
102
-
103
- const headers: Record<string, string> = {}
104
- if (hasBody && !hasFormData) {
105
- headers["content-type"] = "application/json"
106
- }
107
-
108
- const qs = new URLSearchParams()
109
- if (!hasBody) {
110
- Object.entries((params ?? {}) as Record<string, string>).forEach(
111
- ([key, value]) => {
112
- qs.append(key, value)
113
- },
114
- )
115
- } else if (hasFormData) {
116
- body = options.body!
117
- if (params) {
118
- ;(body as FormData).append("payload_json", JSON.stringify(params))
119
- }
120
- } else if (params) {
121
- body = JSON.stringify(params)
122
- } else {
123
- body = options.body!
124
- }
125
-
126
- return request(`${url}?${qs.toString()}`, {
127
- method,
128
- headers,
129
- body,
130
- })
131
- }),
132
- )
@@ -1,14 +0,0 @@
1
- import { DiscordREST } from "./index.js"
2
-
3
- export interface ResponseWithData<A> {
4
- response: Response
5
- json: Effect<never, Http.JsonParseError, A>
6
- text: Effect<never, never, string>
7
- blob: Effect<never, Http.BlobError, Blob>
8
- }
9
-
10
- export type RestResponse<T> = Effect<
11
- DiscordREST,
12
- Http.FetchError | Http.StatusCodeError | Http.JsonParseError,
13
- ResponseWithData<T>
14
- >
@@ -1,35 +0,0 @@
1
- const majorResources = ["channels", "guilds", "webhooks"] as const
2
-
3
- export const routeFromConfig = (path: string, init: RequestInit) => {
4
- const method = (init?.method ?? "get").toLowerCase()
5
-
6
- // Only keep major ID's
7
- const routeURL = path
8
- .split("?")[0]
9
- .replace(/\/([A-Za-z]+)\/(\d{16,21}|@me)/g, (match, resource) =>
10
- majorResources.includes(resource) ? match : `/${resource}`,
11
- )
12
- // Strip reactions
13
- .replace(/\/reactions\/(.*)/, "/reactions")
14
-
15
- return `${method}-${routeURL}`
16
- }
17
-
18
- export const numberHeader = (headers: Headers) => (key: string) =>
19
- Maybe.fromNullable(headers.get(key))
20
- .map(parseFloat)
21
- .filter((n) => !isNaN(n))
22
-
23
- export const retryAfter = (headers: Headers) =>
24
- numberHeader(headers)("x-ratelimit-reset-after")
25
- .catchAll(() => numberHeader(headers)("retry-after"))
26
- .map(Duration.seconds)
27
-
28
- export const rateLimitFromHeaders = (headers: Headers) =>
29
- Maybe.struct({
30
- bucket: Maybe.fromNullable(headers.get("x-ratelimit-bucket")),
31
- retryAfter: retryAfter(headers),
32
- limit: numberHeader(headers)("x-ratelimit-limit"),
33
- remaining: numberHeader(headers)("x-ratelimit-remaining"),
34
- })
35
- export type RateLimitDetails = ReturnType<typeof rateLimitFromHeaders>
@@ -1,68 +0,0 @@
1
- export type Flags<T extends number | bigint> = Record<string, T>
2
-
3
- /**
4
- * Returns all the flags OR'ed together.
5
- */
6
- export function all(flags: Flags<number>): number
7
- export function all(flags: Flags<bigint>): bigint
8
- export function all(flags: Flags<any>): any {
9
- return Object.values(flags).reduce((acc, flag) => acc | flag)
10
- }
11
-
12
- /**
13
- * Returns a function that converts a bitfield to a list of flag names.
14
- */
15
- export function toList<T extends Flags<number>>(
16
- flags: T,
17
- ): (bitfield: number) => (keyof T)[]
18
- export function toList<T extends Flags<bigint>>(
19
- flags: T,
20
- ): (bitfield: bigint) => (keyof T)[]
21
- export function toList<T extends Flags<any>>(
22
- flags: T,
23
- ): (bitfield: any) => (keyof T)[] {
24
- const entries = Object.entries(flags)
25
- return (val) =>
26
- entries.reduce(
27
- (acc, [key, flag]) => ((val & flag) === flag ? [...acc, key] : acc),
28
- [] as (keyof T)[],
29
- )
30
- }
31
-
32
- /**
33
- * Returns a function that converts a list of flags names to a bigint bitfield.
34
- */
35
- export const fromListBigint =
36
- <T extends Flags<bigint>>(flags: T) =>
37
- (list: (keyof T)[]) =>
38
- list.reduce((acc, key) => acc | flags[key], BigInt(0))
39
-
40
- /**
41
- * Returns a function that converts a list of flags names to a bitfield.
42
- */
43
- export const fromList =
44
- <T extends Flags<number>>(flags: T) =>
45
- (list: (keyof T)[]) =>
46
- list.reduce((acc, key) => acc | flags[key], 0)
47
-
48
- /**
49
- * Checks if a bigint bitfield contains and a flag value.
50
- */
51
- export const hasBigInt = (flag: bigint | string) => {
52
- const flagBigInt = BigInt(flag)
53
- return (bits: bigint | string) => {
54
- const bitsBigInt = BigInt(bits)
55
- return (bitsBigInt & flagBigInt) === flagBigInt
56
- }
57
- }
58
-
59
- /**
60
- * Checks if a bitfield contains and a flag value.
61
- */
62
- export const has = (flag: number | string) => {
63
- const flagNumber = +flag
64
- return (bits: number | string) => {
65
- const bitsNumber = +bits
66
- return (bitsNumber & flagNumber) === flagNumber
67
- }
68
- }