yuzuha 0.2.0 → 0.2.1

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 (169) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/VERSIONING.md +29 -7
  3. package/dist/commands/context.d.ts.map +1 -1
  4. package/dist/commands/context.js +6 -1
  5. package/dist/commands/context.js.map +1 -1
  6. package/dist/index.d.ts +6 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/infrastructure/fluxer/resources/cache.d.ts +4 -0
  10. package/dist/infrastructure/fluxer/resources/cache.d.ts.map +1 -0
  11. package/dist/infrastructure/fluxer/resources/cache.js +51 -0
  12. package/dist/infrastructure/fluxer/resources/cache.js.map +1 -0
  13. package/dist/infrastructure/fluxer/resources/collectors.d.ts.map +1 -1
  14. package/dist/infrastructure/fluxer/resources/collectors.js +86 -4
  15. package/dist/infrastructure/fluxer/resources/collectors.js.map +1 -1
  16. package/dist/infrastructure/fluxer/resources/gateway.d.ts +4 -0
  17. package/dist/infrastructure/fluxer/resources/gateway.d.ts.map +1 -0
  18. package/dist/infrastructure/fluxer/resources/gateway.js +31 -0
  19. package/dist/infrastructure/fluxer/resources/gateway.js.map +1 -0
  20. package/dist/infrastructure/fluxer/resources/instance.d.ts +4 -0
  21. package/dist/infrastructure/fluxer/resources/instance.d.ts.map +1 -0
  22. package/dist/infrastructure/fluxer/resources/instance.js +96 -0
  23. package/dist/infrastructure/fluxer/resources/instance.js.map +1 -0
  24. package/dist/infrastructure/fluxer/resources/messages.d.ts +3 -1
  25. package/dist/infrastructure/fluxer/resources/messages.d.ts.map +1 -1
  26. package/dist/infrastructure/fluxer/resources/messages.js +49 -35
  27. package/dist/infrastructure/fluxer/resources/messages.js.map +1 -1
  28. package/dist/infrastructure/fluxer/resources/self.d.ts +4 -0
  29. package/dist/infrastructure/fluxer/resources/self.d.ts.map +1 -0
  30. package/dist/infrastructure/fluxer/resources/self.js +36 -0
  31. package/dist/infrastructure/fluxer/resources/self.js.map +1 -0
  32. package/dist/infrastructure/fluxer/resources/users.d.ts +4 -2
  33. package/dist/infrastructure/fluxer/resources/users.d.ts.map +1 -1
  34. package/dist/infrastructure/fluxer/resources/users.js +2 -2
  35. package/dist/infrastructure/fluxer/resources/users.js.map +1 -1
  36. package/dist/infrastructure/fluxer/resources.d.ts +4 -0
  37. package/dist/infrastructure/fluxer/resources.d.ts.map +1 -1
  38. package/dist/infrastructure/fluxer/resources.js +12 -0
  39. package/dist/infrastructure/fluxer/resources.js.map +1 -1
  40. package/dist/ports/resources/cache.d.ts +52 -0
  41. package/dist/ports/resources/cache.d.ts.map +1 -0
  42. package/dist/ports/resources/cache.js +2 -0
  43. package/dist/ports/resources/cache.js.map +1 -0
  44. package/dist/ports/resources/collectors.d.ts +18 -0
  45. package/dist/ports/resources/collectors.d.ts.map +1 -1
  46. package/dist/ports/resources/collectors.js.map +1 -1
  47. package/dist/ports/resources/gateway.d.ts +48 -0
  48. package/dist/ports/resources/gateway.d.ts.map +1 -0
  49. package/dist/ports/resources/gateway.js +2 -0
  50. package/dist/ports/resources/gateway.js.map +1 -0
  51. package/dist/ports/resources/instance.d.ts +101 -0
  52. package/dist/ports/resources/instance.d.ts.map +1 -0
  53. package/dist/ports/resources/instance.js +2 -0
  54. package/dist/ports/resources/instance.js.map +1 -0
  55. package/dist/ports/resources/messages.d.ts +20 -0
  56. package/dist/ports/resources/messages.d.ts.map +1 -1
  57. package/dist/ports/resources/messages.js.map +1 -1
  58. package/dist/ports/resources/self.d.ts +30 -0
  59. package/dist/ports/resources/self.d.ts.map +1 -0
  60. package/dist/ports/resources/self.js +2 -0
  61. package/dist/ports/resources/self.js.map +1 -0
  62. package/dist/ports/resources.d.ts +8 -0
  63. package/dist/ports/resources.d.ts.map +1 -1
  64. package/dist/ports/resources.js.map +1 -1
  65. package/dist/sdk/builders.d.ts +2 -0
  66. package/dist/sdk/builders.d.ts.map +1 -0
  67. package/dist/sdk/builders.js +2 -0
  68. package/dist/sdk/builders.js.map +1 -0
  69. package/dist/sdk/client.d.ts +2 -0
  70. package/dist/sdk/client.d.ts.map +1 -0
  71. package/dist/sdk/client.js +2 -0
  72. package/dist/sdk/client.js.map +1 -0
  73. package/dist/sdk/cluster.d.ts +2 -0
  74. package/dist/sdk/cluster.d.ts.map +1 -0
  75. package/dist/sdk/cluster.js +2 -0
  76. package/dist/sdk/cluster.js.map +1 -0
  77. package/dist/sdk/collection.d.ts +2 -0
  78. package/dist/sdk/collection.d.ts.map +1 -0
  79. package/dist/sdk/collection.js +2 -0
  80. package/dist/sdk/collection.js.map +1 -0
  81. package/dist/sdk/errors.d.ts +2 -0
  82. package/dist/sdk/errors.d.ts.map +1 -0
  83. package/dist/sdk/errors.js +2 -0
  84. package/dist/sdk/errors.js.map +1 -0
  85. package/dist/sdk/internal.d.ts +2 -0
  86. package/dist/sdk/internal.d.ts.map +1 -0
  87. package/dist/sdk/internal.js +2 -0
  88. package/dist/sdk/internal.js.map +1 -0
  89. package/dist/sdk/message.d.ts +2 -0
  90. package/dist/sdk/message.d.ts.map +1 -0
  91. package/dist/sdk/message.js +2 -0
  92. package/dist/sdk/message.js.map +1 -0
  93. package/dist/sdk/rest/request-manager.d.ts +2 -0
  94. package/dist/sdk/rest/request-manager.d.ts.map +1 -0
  95. package/dist/sdk/rest/request-manager.js +2 -0
  96. package/dist/sdk/rest/request-manager.js.map +1 -0
  97. package/dist/sdk/rest.d.ts +2 -0
  98. package/dist/sdk/rest.d.ts.map +1 -0
  99. package/dist/sdk/rest.js +2 -0
  100. package/dist/sdk/rest.js.map +1 -0
  101. package/dist/sdk/sharding.d.ts +2 -0
  102. package/dist/sdk/sharding.d.ts.map +1 -0
  103. package/dist/sdk/sharding.js +2 -0
  104. package/dist/sdk/sharding.js.map +1 -0
  105. package/dist/sdk/types/routes.d.ts +2 -0
  106. package/dist/sdk/types/routes.d.ts.map +1 -0
  107. package/dist/sdk/types/routes.js +2 -0
  108. package/dist/sdk/types/routes.js.map +1 -0
  109. package/dist/sdk/types.d.ts +2 -0
  110. package/dist/sdk/types.d.ts.map +1 -0
  111. package/dist/sdk/types.js +2 -0
  112. package/dist/sdk/types.js.map +1 -0
  113. package/dist/sdk/util.d.ts +2 -0
  114. package/dist/sdk/util.d.ts.map +1 -0
  115. package/dist/sdk/util.js +2 -0
  116. package/dist/sdk/util.js.map +1 -0
  117. package/dist/sdk/voice.d.ts +2 -0
  118. package/dist/sdk/voice.d.ts.map +1 -0
  119. package/dist/sdk/voice.js +2 -0
  120. package/dist/sdk/voice.js.map +1 -0
  121. package/dist/sdk/ws.d.ts +2 -0
  122. package/dist/sdk/ws.d.ts.map +1 -0
  123. package/dist/sdk/ws.js +2 -0
  124. package/dist/sdk/ws.js.map +1 -0
  125. package/dist/services/resource-authorization.d.ts.map +1 -1
  126. package/dist/services/resource-authorization.js +2 -1
  127. package/dist/services/resource-authorization.js.map +1 -1
  128. package/dist/testing.d.ts.map +1 -1
  129. package/dist/testing.js +4 -0
  130. package/dist/testing.js.map +1 -1
  131. package/docs/FUNCTION-GAPS.md +8 -3
  132. package/docs/NATIVE.md +32 -5
  133. package/docs/PARITY-MATRIX.md +25 -25
  134. package/docs/PARITY.md +21 -9
  135. package/package.json +22 -1
  136. package/src/commands/context.ts +6 -1
  137. package/src/index.ts +8 -2
  138. package/src/infrastructure/fluxer/resources/cache.ts +52 -0
  139. package/src/infrastructure/fluxer/resources/collectors.ts +87 -4
  140. package/src/infrastructure/fluxer/resources/gateway.ts +33 -0
  141. package/src/infrastructure/fluxer/resources/instance.ts +105 -0
  142. package/src/infrastructure/fluxer/resources/messages.ts +49 -35
  143. package/src/infrastructure/fluxer/resources/self.ts +39 -0
  144. package/src/infrastructure/fluxer/resources/users.ts +2 -2
  145. package/src/infrastructure/fluxer/resources.ts +12 -0
  146. package/src/ports/resources/cache.ts +56 -0
  147. package/src/ports/resources/collectors.ts +20 -0
  148. package/src/ports/resources/gateway.ts +49 -0
  149. package/src/ports/resources/instance.ts +109 -0
  150. package/src/ports/resources/messages.ts +23 -0
  151. package/src/ports/resources/self.ts +30 -0
  152. package/src/ports/resources.ts +8 -0
  153. package/src/sdk/builders.ts +1 -0
  154. package/src/sdk/client.ts +1 -0
  155. package/src/sdk/cluster.ts +1 -0
  156. package/src/sdk/collection.ts +1 -0
  157. package/src/sdk/errors.ts +1 -0
  158. package/src/sdk/internal.ts +1 -0
  159. package/src/sdk/message.ts +1 -0
  160. package/src/sdk/rest/request-manager.ts +1 -0
  161. package/src/sdk/rest.ts +1 -0
  162. package/src/sdk/sharding.ts +1 -0
  163. package/src/sdk/types/routes.ts +1 -0
  164. package/src/sdk/types.ts +1 -0
  165. package/src/sdk/util.ts +1 -0
  166. package/src/sdk/voice.ts +1 -0
  167. package/src/sdk/ws.ts +1 -0
  168. package/src/services/resource-authorization.ts +2 -1
  169. package/src/testing.ts +4 -0
@@ -0,0 +1,105 @@
1
+ import type { Client } from '@fluxerjs/core';
2
+ import type { APIApplicationMe, APIInstance, APIInstanceEndpoints, APIOAuthApplication } from '@fluxerjs/types';
3
+ import type {
4
+ ApplicationInfo,
5
+ InstanceInfo,
6
+ InstanceResources,
7
+ } from '../../../ports/resources/instance.js';
8
+
9
+ function instanceInfo(endpoints: APIInstanceEndpoints, discovery: APIInstance | null): InstanceInfo {
10
+ return {
11
+ apiCodeVersion: discovery?.api_code_version ?? null,
12
+ codename: discovery?.codename ?? null,
13
+ endpoints: {
14
+ api: endpoints.api,
15
+ apiClient: endpoints.api_client,
16
+ apiPublic: endpoints.api_public,
17
+ gateway: endpoints.gateway,
18
+ media: endpoints.media,
19
+ staticCDN: endpoints.static_cdn,
20
+ marketing: endpoints.marketing,
21
+ admin: endpoints.admin,
22
+ invite: endpoints.invite,
23
+ gift: endpoints.gift,
24
+ webapp: endpoints.webapp,
25
+ },
26
+ features: discovery?.features ? {
27
+ voiceEnabled: discovery.features.voice_enabled,
28
+ stripeEnabled: discovery.features.stripe_enabled,
29
+ selfHosted: discovery.features.self_hosted,
30
+ presignedAttachmentUploads: discovery.features.presigned_attachment_uploads,
31
+ emailsEnabled: discovery.features.emails_enabled,
32
+ } : null,
33
+ registrationMode: discovery?.registration?.mode ?? null,
34
+ ssoEnabled: discovery?.sso?.enabled ?? null,
35
+ ssoEnforced: discovery?.sso?.enforced ?? null,
36
+ singleCommunity: discovery?.community?.single_community ?? null,
37
+ singleCommunityGuildID: discovery?.community?.single_community_guild_id ?? null,
38
+ directMessagesDisabled: discovery?.community?.direct_messages_disabled ?? null,
39
+ services: discovery?.services ? {
40
+ gif: discovery.services.gif_enabled,
41
+ youtube: discovery.services.youtube_enabled,
42
+ bluesky: discovery.services.bluesky_enabled,
43
+ } : null,
44
+ };
45
+ }
46
+
47
+ function applicationInfo(application: APIApplicationMe | APIOAuthApplication): ApplicationInfo {
48
+ return {
49
+ id: application.id,
50
+ name: application.name,
51
+ icon: application.icon ?? null,
52
+ description: application.description ?? null,
53
+ botPublic: application.bot_public ?? null,
54
+ botRequireCodeGrant: application.bot_require_code_grant ?? null,
55
+ verifyKey: 'verify_key' in application ? application.verify_key ?? null : null,
56
+ flags: 'flags' in application ? application.flags ?? null : null,
57
+ ownerID: 'owner' in application ? application.owner?.id ?? null : null,
58
+ botID: 'bot' in application ? application.bot?.id ?? null : null,
59
+ redirectURIs: 'redirect_uris' in application ? [...application.redirect_uris ?? []] : [],
60
+ clientSecret: 'client_secret' in application ? application.client_secret ?? null : null,
61
+ };
62
+ }
63
+
64
+ export function createInstanceResources(client: Client): InstanceResources {
65
+ return {
66
+ async describe() {
67
+ const resolved = client.instance;
68
+ const fetched = await client.fetchInstance();
69
+ return instanceInfo(fetched?.endpoints ?? resolved.endpoints, fetched ?? resolved.discovery);
70
+ },
71
+ async gatewayInfo() {
72
+ const info = await client.fetchGatewayInfo();
73
+ const limit = info.session_start_limit;
74
+ return {
75
+ url: info.url,
76
+ shards: info.shards,
77
+ sessionStartLimit: limit ? {
78
+ total: limit.total,
79
+ remaining: limit.remaining,
80
+ resetAfter: limit.reset_after,
81
+ maxConcurrency: limit.max_concurrency,
82
+ } : null,
83
+ };
84
+ },
85
+ async application() {
86
+ return applicationInfo(await client.fetchApplication());
87
+ },
88
+ async oauthApplications() {
89
+ return (await client.fetchOAuthApplications()).map(applicationInfo);
90
+ },
91
+ async emojiMetadata(emojiID) {
92
+ const metadata = await client.fetchEmojiMetadata(emojiID);
93
+ return { id: metadata.id, guildID: metadata.guild_id, name: metadata.name,
94
+ animated: metadata.animated, allowCloning: metadata.allow_cloning };
95
+ },
96
+ async stickerMetadata(stickerID) {
97
+ const metadata = await client.fetchStickerMetadata(stickerID);
98
+ return { id: metadata.id, guildID: metadata.guild_id, name: metadata.name,
99
+ animated: metadata.animated, allowCloning: metadata.allow_cloning };
100
+ },
101
+ resolveEmoji(emoji, guildID) {
102
+ return client.resolveEmoji(emoji, guildID);
103
+ },
104
+ };
105
+ }
@@ -4,7 +4,7 @@ import type { ReceivedEmbed, ReceivedEmbedMedia } from '../../../domain/messages
4
4
  import { limitOf } from '../../../ports/resources/common.js';
5
5
  import type { AttachmentSpec, BinaryAttachmentSpec } from '../../../ports/resources/common.js';
6
6
  import { YuzuhaError } from '../../../errors.js';
7
- import type { MessageInfo, MessagesResources } from '../../../ports/resources/messages.js';
7
+ import type { BulkFetchRawChannel, MessageInfo, MessageSearchInput, MessageSearchPage, MessagesResources } from '../../../ports/resources/messages.js';
8
8
  import { preloadChannels } from '../../../domain/resource-batches.js';
9
9
  import { messageSearchInput, pinnedMessagesInput, reactionUsersInput } from '../../../domain/message-query.js';
10
10
 
@@ -27,7 +27,7 @@ function receivedEmbed(embed: MessageEmbed): ReceivedEmbed {
27
27
  };
28
28
  }
29
29
 
30
- function info(message: Message): MessageInfo {
30
+ export function toMessageInfo(message: Message): MessageInfo {
31
31
  return {
32
32
  id: message.id, channelID: message.channelId, authorID: message.author.id,
33
33
  authorName: message.author.username, content: message.content,
@@ -59,14 +59,43 @@ export function createMessagesResources(client: Client): MessagesResources {
59
59
  allowedMentions: { parse: [], repliedUser: false },
60
60
  })).id;
61
61
  };
62
+ const search = async (rawInput: MessageSearchInput): Promise<MessageSearchPage> => {
63
+ const input = messageSearchInput(rawInput);
64
+ const result = await client.searchMessages({ content: input.content,
65
+ contextGuildId: input.guildID, contextChannelId: input.channelID,
66
+ channelIds: input.channelIDs ?? (input.channelID ? [input.channelID] : undefined),
67
+ authorIds: input.authorIDs ?? (input.authorID ? [input.authorID] : undefined),
68
+ hitsPerPage: limitOf(input.limit, 25, 100), cursor: input.cursor,
69
+ ...(input.contents === undefined ? {} : { contents: input.contents }),
70
+ ...(input.exactPhrases === undefined ? {} : { exactPhrases: input.exactPhrases }),
71
+ ...(input.excludeChannelIDs === undefined ? {} : { excludeChannelIds: input.excludeChannelIDs }),
72
+ ...(input.excludeAuthorIDs === undefined ? {} : { excludeAuthorIds: input.excludeAuthorIDs }),
73
+ ...(input.authorType === undefined ? {} : { authorType: input.authorType }),
74
+ ...(input.excludeAuthorType === undefined ? {} : { excludeAuthorType: input.excludeAuthorType }),
75
+ ...(input.mentions === undefined ? {} : { mentions: input.mentions }),
76
+ ...(input.excludeMentions === undefined ? {} : { excludeMentions: input.excludeMentions }),
77
+ ...(input.mentionEveryone === undefined ? {} : { mentionEveryone: input.mentionEveryone }),
78
+ ...(input.pinned === undefined ? {} : { pinned: input.pinned }),
79
+ ...(input.includeNsfw === undefined ? {} : { includeNsfw: input.includeNsfw }),
80
+ ...(input.has === undefined ? {} : { has: input.has }),
81
+ ...(input.excludeHas === undefined ? {} : { excludeHas: input.excludeHas }),
82
+ ...(input.minID === undefined ? {} : { minId: input.minID }),
83
+ ...(input.maxID === undefined ? {} : { maxId: input.maxID }),
84
+ ...(input.sortBy === undefined ? {} : { sortBy: input.sortBy }),
85
+ ...(input.sortOrder === undefined ? {} : { sortOrder: input.sortOrder }),
86
+ ...(input.page === undefined ? {} : { page: input.page }) });
87
+ if ('indexing' in result) return { indexing: true, messages: [], total: 0, cursor: null };
88
+ return { indexing: false, messages: result.messages.map(data => toMessageInfo(new Message(client, data))),
89
+ total: result.total, cursor: result.cursor ?? null };
90
+ };
62
91
  return {
63
92
  async preload(channelIDs) {
64
93
  const ids = preloadChannels(channelIDs);
65
94
  const result = await client.preloadMessages(ids);
66
- return Object.fromEntries(ids.map(id => [id, result[id] ? info(new Message(client, result[id])) : null]));
95
+ return Object.fromEntries(ids.map(id => [id, result[id] ? toMessageInfo(new Message(client, result[id])) : null]));
67
96
  },
68
97
  async fetch(channelID: string, messageID: string): Promise<MessageInfo> {
69
- return info(await client.channels.fetchMessage(channelID, messageID));
98
+ return toMessageInfo(await client.channels.fetchMessage(channelID, messageID));
70
99
  },
71
100
  async list(channelID, limit, cursor = {}): Promise<MessageInfo[]> {
72
101
  if (Object.values(cursor).filter(value => value !== undefined).length > 1) {
@@ -75,7 +104,7 @@ export function createMessagesResources(client: Client): MessagesResources {
75
104
  const channel = await client.channels.fetch(channelID, { force: true });
76
105
  if (!channel.isTextBased()) throw new YuzuhaError('CHANNEL_TEXT', 'Canal sem suporte a mensagens.');
77
106
  const messages = await channel.messages.fetch({ limit: limitOf(limit, 20, 100), ...cursor });
78
- return order([...messages.values()].map(info));
107
+ return order([...messages.values()].map(toMessageInfo));
79
108
  },
80
109
  async pin(channelID: string, messageID: string): Promise<void> {
81
110
  await (await client.channels.fetchMessage(channelID, messageID)).pin();
@@ -90,14 +119,14 @@ export function createMessagesResources(client: Client): MessagesResources {
90
119
  const messages = limit === undefined
91
120
  ? await channel.fetchPinnedMessages()
92
121
  : await channel.fetchPinnedMessagesPage({ limit }).then(page => page.messages);
93
- return order(messages.map(info));
122
+ return order(messages.map(toMessageInfo));
94
123
  },
95
124
  async pinnedPage(channelID, input) {
96
125
  const options = pinnedMessagesInput(input);
97
126
  const channel = await client.channels.fetch(channelID, { force: true });
98
127
  if (!channel.isTextBased()) throw new YuzuhaError('CHANNEL_TEXT', 'Canal sem suporte a mensagens.');
99
128
  const page = await channel.fetchPinnedMessagesPage(options);
100
- return { messages: page.messages.map(info), pinnedAt: [...page.pinnedAt], hasMore: page.hasMore,
129
+ return { messages: page.messages.map(toMessageInfo), pinnedAt: [...page.pinnedAt], hasMore: page.hasMore,
101
130
  nextBefore: page.hasMore ? page.pinnedAt.at(-1) ?? null : null };
102
131
  },
103
132
  sendFiles: send,
@@ -111,34 +140,19 @@ export function createMessagesResources(client: Client): MessagesResources {
111
140
  allowedMentions: { parse: [], repliedUser: false },
112
141
  })).id;
113
142
  },
114
- async search(rawInput) {
115
- const input = messageSearchInput(rawInput);
116
- const result = await client.searchMessages({ content: input.content,
117
- contextGuildId: input.guildID, contextChannelId: input.channelID,
118
- channelIds: input.channelIDs ?? (input.channelID ? [input.channelID] : undefined),
119
- authorIds: input.authorIDs ?? (input.authorID ? [input.authorID] : undefined),
120
- hitsPerPage: limitOf(input.limit, 25, 100), cursor: input.cursor,
121
- ...(input.contents === undefined ? {} : { contents: input.contents }),
122
- ...(input.exactPhrases === undefined ? {} : { exactPhrases: input.exactPhrases }),
123
- ...(input.excludeChannelIDs === undefined ? {} : { excludeChannelIds: input.excludeChannelIDs }),
124
- ...(input.excludeAuthorIDs === undefined ? {} : { excludeAuthorIds: input.excludeAuthorIDs }),
125
- ...(input.authorType === undefined ? {} : { authorType: input.authorType }),
126
- ...(input.excludeAuthorType === undefined ? {} : { excludeAuthorType: input.excludeAuthorType }),
127
- ...(input.mentions === undefined ? {} : { mentions: input.mentions }),
128
- ...(input.excludeMentions === undefined ? {} : { excludeMentions: input.excludeMentions }),
129
- ...(input.mentionEveryone === undefined ? {} : { mentionEveryone: input.mentionEveryone }),
130
- ...(input.pinned === undefined ? {} : { pinned: input.pinned }),
131
- ...(input.includeNsfw === undefined ? {} : { includeNsfw: input.includeNsfw }),
132
- ...(input.has === undefined ? {} : { has: input.has }),
133
- ...(input.excludeHas === undefined ? {} : { excludeHas: input.excludeHas }),
134
- ...(input.minID === undefined ? {} : { minId: input.minID }),
135
- ...(input.maxID === undefined ? {} : { maxId: input.maxID }),
136
- ...(input.sortBy === undefined ? {} : { sortBy: input.sortBy }),
137
- ...(input.sortOrder === undefined ? {} : { sortOrder: input.sortOrder }),
138
- ...(input.page === undefined ? {} : { page: input.page }) });
139
- if ('indexing' in result) return { indexing: true, messages: [], total: 0, cursor: null };
140
- return { indexing: false, messages: result.messages.map(data => info(new Message(client, data))),
141
- total: result.total, cursor: result.cursor ?? null };
143
+ search,
144
+ searchAll: search,
145
+ async bulkFetch(requests, input): Promise<MessageInfo[] | BulkFetchRawChannel[]> {
146
+ const options = requests.map(request => ({ channelId: request.channelID, limit: request.limit,
147
+ ...(request.before === undefined ? {} : { before: request.before }),
148
+ ...(request.after === undefined ? {} : { after: request.after }),
149
+ ...(request.around === undefined ? {} : { around: request.around }) }));
150
+ if (input?.hydrate === false) {
151
+ const raw = await client.bulkFetchMessages(options, { hydrate: false });
152
+ return raw.channels.map(channel => ({ channelID: channel.channel_id, messages: channel.messages }));
153
+ }
154
+ const result = await client.bulkFetchMessages(options);
155
+ return result.channels.flatMap(channel => [...channel.messages.values()].map(toMessageInfo));
142
156
  },
143
157
  sendBytes: send,
144
158
  sendAttachments: send,
@@ -0,0 +1,39 @@
1
+ import type { Client, ClientUser } from '@fluxerjs/core';
2
+ import { YuzuhaError } from '../../../errors.js';
3
+ import type { SelfResources } from '../../../ports/resources/self.js';
4
+ import { toUserInfo } from './users.js';
5
+
6
+ function selfUser(client: Client): ClientUser {
7
+ const user = client.user;
8
+ if (!user) {
9
+ throw new YuzuhaError('NOT_READY', 'O cliente ainda não está pronto; o usuário autenticado não está disponível.');
10
+ }
11
+ return user;
12
+ }
13
+
14
+ export function createSelfResources(client: Client): SelfResources {
15
+ return {
16
+ async fetch() {
17
+ return toUserInfo(await selfUser(client).fetch());
18
+ },
19
+ async guilds(input) {
20
+ const guilds = await selfUser(client).fetchGuilds({ withCounts: input?.withCounts ?? false });
21
+ return guilds.map(guild => ({
22
+ id: guild.id,
23
+ name: guild.name,
24
+ icon: guild.icon,
25
+ owner: guild.owner ?? false,
26
+ permissions: guild.permissions ?? null,
27
+ features: guild.features ?? [],
28
+ memberCount: guild.approximateMemberCount ?? null,
29
+ onlineCount: guild.approximatePresenceCount ?? null,
30
+ }));
31
+ },
32
+ async leaveGuild(guildID, verification) {
33
+ await selfUser(client).leaveGuild(guildID, verification);
34
+ },
35
+ async deleteMyMessages(guildID, verification) {
36
+ await selfUser(client).bulkDeleteMyMessagesInGuild(guildID, verification);
37
+ },
38
+ };
39
+ }
@@ -65,7 +65,7 @@ function snowflakeTimestamp(id: string): number {
65
65
  return Number((BigInt(id) >> 22n) + FLUXER_EPOCH);
66
66
  }
67
67
 
68
- function toUserInfo(user: User): UserInfo {
68
+ export function toUserInfo(user: User): UserInfo {
69
69
  return {
70
70
  id: user.id,
71
71
  username: user.username,
@@ -77,7 +77,7 @@ function toUserInfo(user: User): UserInfo {
77
77
  };
78
78
  }
79
79
 
80
- function toMemberInfo(member: GuildMember): MemberInfo {
80
+ export function toMemberInfo(member: GuildMember): MemberInfo {
81
81
  const user = member.user;
82
82
  return {
83
83
  id: member.id,
@@ -1,11 +1,15 @@
1
1
  import type { Client } from '@fluxerjs/core';
2
2
  import type { Resources } from '../../ports/resources.js';
3
+ import { createCacheResources } from './resources/cache.js';
3
4
  import { createChannelsResources } from './resources/channels.js';
4
5
  import { createCollectorsResources } from './resources/collectors.js';
5
6
  import { createExpressionsResources } from './resources/expressions.js';
7
+ import { createGatewayResources } from './resources/gateway.js';
8
+ import { createInstanceResources } from './resources/instance.js';
6
9
  import { createInvitesResources } from './resources/invites.js';
7
10
  import { createMessagesResources } from './resources/messages.js';
8
11
  import { createRolesResources } from './resources/roles.js';
12
+ import { createSelfResources } from './resources/self.js';
9
13
  import { createThreadsResources } from './resources/threads.js';
10
14
  import { createUsersResources } from './resources/users.js';
11
15
  import { createWebhooksResources } from './resources/webhooks.js';
@@ -21,6 +25,10 @@ export class FluxerResources implements Resources {
21
25
  readonly expressions;
22
26
  readonly threads;
23
27
  readonly invites;
28
+ readonly gateway;
29
+ readonly instance;
30
+ readonly self;
31
+ readonly cache;
24
32
 
25
33
  constructor(client: Client) {
26
34
  this.users = createUsersResources(client);
@@ -32,5 +40,9 @@ export class FluxerResources implements Resources {
32
40
  this.expressions = createExpressionsResources(client);
33
41
  this.threads = createThreadsResources(client);
34
42
  this.invites = createInvitesResources(client);
43
+ this.gateway = createGatewayResources(client);
44
+ this.instance = createInstanceResources(client);
45
+ this.self = createSelfResources(client);
46
+ this.cache = createCacheResources(client);
35
47
  }
36
48
  }
@@ -0,0 +1,56 @@
1
+ import type { MessageInfo } from './messages.js';
2
+ import type { MemberInfo } from './users.js';
3
+
4
+ /** Limites resolvidos do cache; `null` indica ilimitado. */
5
+ export interface CacheLimitsInfo {
6
+ guilds: number | null;
7
+ channels: number | null;
8
+ users: number | null;
9
+ members: number | null;
10
+ /** `false` quando o cache de mensagens está desabilitado. */
11
+ messages: number | false | null;
12
+ roles: number | null;
13
+ emojis: number | null;
14
+ stickers: number | null;
15
+ }
16
+
17
+ /** Tamanhos atuais dos buckets de cache com os limites aplicados. */
18
+ export interface CacheStatsInfo {
19
+ guilds: number;
20
+ channels: number;
21
+ users: number;
22
+ members: number;
23
+ messages: number;
24
+ messageChannels: number;
25
+ roles: number;
26
+ emojis: number;
27
+ stickers: number;
28
+ limits: CacheLimitsInfo;
29
+ }
30
+
31
+ /**
32
+ * Filtro de limpeza do cache de mensagens. Sem `maxAgeMs` nem `keep`,
33
+ * todas as mensagens do escopo são removidas. `keep` retorna verdadeiro
34
+ * para manter a mensagem no cache.
35
+ */
36
+ export interface MessageSweepInput {
37
+ channelID?: string;
38
+ maxAgeMs?: number;
39
+ keep?: (message: MessageInfo, channelID: string) => boolean;
40
+ }
41
+
42
+ /**
43
+ * Filtro de limpeza do cache de membros. Sem `keep`, todos os membros
44
+ * do escopo são removidos. `keep` retorna verdadeiro para manter o membro.
45
+ */
46
+ export interface MemberSweepInput {
47
+ guildID?: string;
48
+ keep?: (member: MemberInfo, guildID: string) => boolean;
49
+ }
50
+
51
+ /** Inspeção e limpeza dos caches locais do client. */
52
+ export interface CacheResources {
53
+ stats(): Promise<CacheStatsInfo>;
54
+ sweepMessages(input?: MessageSweepInput): Promise<number>;
55
+ sweepMembers(input?: MemberSweepInput): Promise<number>;
56
+ }
@@ -5,6 +5,23 @@ export interface AwaitMessageInput {
5
5
  signal?: AbortSignal;
6
6
  }
7
7
 
8
+ export interface CollectMessagesInput {
9
+ userID?: string;
10
+ contains?: string;
11
+ timeMs?: number;
12
+ max?: number;
13
+ signal?: AbortSignal;
14
+ }
15
+
16
+ export interface CollectReactionsInput {
17
+ messageID: string;
18
+ emojis?: string | string[];
19
+ userID?: string;
20
+ timeMs?: number;
21
+ max?: number;
22
+ signal?: AbortSignal;
23
+ }
24
+
8
25
  export interface CollectedMessageInfo {
9
26
  id: string;
10
27
  channelID: string;
@@ -15,10 +32,13 @@ export interface CollectedMessageInfo {
15
32
  export interface ReactionVote {
16
33
  userID: string;
17
34
  emoji: string;
35
+ messageID?: string;
18
36
  }
19
37
 
20
38
  export interface CollectorsResources {
21
39
  awaitMessage(channelID: string, input: AwaitMessageInput): Promise<CollectedMessageInfo | null>;
22
40
  awaitReaction(channelID: string, messageID: string, emojis: string | string[],
23
41
  timeMs: number, userID?: string, signal?: AbortSignal): Promise<ReactionVote | null>;
42
+ collectMessages(channelID: string, input: CollectMessagesInput): AsyncIterable<CollectedMessageInfo>;
43
+ collectReactions(channelID: string, input: CollectReactionsInput): AsyncIterable<ReactionVote>;
24
44
  }
@@ -0,0 +1,49 @@
1
+ import type { GatewaySendPayload } from '@fluxerjs/types';
2
+
3
+ /** Pedido de membros de servidor pelo gateway (opcode 8). */
4
+ export interface GatewayMembersRequest {
5
+ guildID?: string;
6
+ guildIDs?: string[];
7
+ query?: string;
8
+ limit?: number;
9
+ userIDs?: string[];
10
+ presences?: boolean;
11
+ nonce?: string;
12
+ }
13
+
14
+ /** Pedido de contagens de membros por canal dentro de um servidor (opcode 16). */
15
+ export interface ChannelCountsRequest {
16
+ guildID: string;
17
+ channelID?: string;
18
+ channelIDs?: string[];
19
+ nonce?: string;
20
+ }
21
+
22
+ /** Pedido de não lidos de posts em um canal de fórum ou mídia (opcode 28). */
23
+ export interface ForumUnreadsRequest {
24
+ guildID: string;
25
+ channelID: string;
26
+ threads: { threadID: string; ackMessageID?: string }[];
27
+ }
28
+
29
+ /** Assinatura de tráfego de threads em um servidor (opcode 14). */
30
+ export interface ThreadSubscriptionInput {
31
+ guildID: string;
32
+ threads?: boolean;
33
+ threadIDs?: string[];
34
+ }
35
+
36
+ /**
37
+ * Pedidos brutos ao gateway. São fire-and-forget: os resultados chegam por
38
+ * eventos do client (ex.: `guildCountsUpdate`, `channelMemberCountsUpdate`,
39
+ * `forumUnreads`, `threadMemberListUpdate`). Para aguardar a resposta use
40
+ * `users.guildCounts` e `users.channelCounts`, que encapsulam pedido e espera.
41
+ */
42
+ export interface GatewayResources {
43
+ requestMembers(input: GatewayMembersRequest): void;
44
+ requestGuildCounts(guildIDs: string[], nonce?: string): void;
45
+ requestChannelCounts(input: ChannelCountsRequest): void;
46
+ requestForumUnreads(input: ForumUnreadsRequest): void;
47
+ subscribeThreads(input: ThreadSubscriptionInput): void;
48
+ send(shardID: number, payload: GatewaySendPayload): void;
49
+ }
@@ -0,0 +1,109 @@
1
+ /** URLs de endpoint resolvidas da instância Fluxer. */
2
+ export interface InstanceEndpointsInfo {
3
+ api: string;
4
+ apiClient: string;
5
+ apiPublic: string;
6
+ gateway: string;
7
+ media: string;
8
+ staticCDN: string;
9
+ marketing: string;
10
+ admin: string;
11
+ invite: string;
12
+ gift: string;
13
+ webapp: string;
14
+ }
15
+
16
+ /** Flags de recursos do documento de descoberta da instância. */
17
+ export interface InstanceFeaturesInfo {
18
+ voiceEnabled: boolean;
19
+ stripeEnabled: boolean;
20
+ selfHosted: boolean;
21
+ presignedAttachmentUploads: boolean;
22
+ emailsEnabled: boolean;
23
+ }
24
+
25
+ /** Integrações opcionais de terceiros habilitadas na instância. */
26
+ export interface InstanceServicesInfo {
27
+ gif: boolean;
28
+ youtube: boolean;
29
+ bluesky: boolean;
30
+ }
31
+
32
+ /** Descrição normalizada da instância: endpoints resolvidos e descoberta. */
33
+ export interface InstanceInfo {
34
+ apiCodeVersion: number | null;
35
+ codename: string | null;
36
+ endpoints: InstanceEndpointsInfo;
37
+ features: InstanceFeaturesInfo | null;
38
+ registrationMode: 'open' | 'approval' | 'closed' | null;
39
+ ssoEnabled: boolean | null;
40
+ ssoEnforced: boolean | null;
41
+ singleCommunity: boolean | null;
42
+ singleCommunityGuildID: string | null;
43
+ directMessagesDisabled: boolean | null;
44
+ services: InstanceServicesInfo | null;
45
+ }
46
+
47
+ /** Informações de conexão do gateway (`GET /gateway/bot`). */
48
+ export interface GatewayInfo {
49
+ url: string;
50
+ shards: number;
51
+ sessionStartLimit: {
52
+ total: number;
53
+ remaining: number;
54
+ resetAfter: number;
55
+ maxConcurrency: number;
56
+ } | null;
57
+ }
58
+
59
+ /** Aplicação normalizada de `applications/@me` e `oauth2/applications/@me`. */
60
+ export interface ApplicationInfo {
61
+ id: string;
62
+ name: string;
63
+ icon: string | null;
64
+ description: string | null;
65
+ botPublic: boolean | null;
66
+ botRequireCodeGrant: boolean | null;
67
+ verifyKey: string | null;
68
+ flags: number | null;
69
+ ownerID: string | null;
70
+ botID: string | null;
71
+ redirectURIs: string[];
72
+ clientSecret: string | null;
73
+ }
74
+
75
+ /** Metadados de emoji (`GET /emojis/{id}/metadata`). */
76
+ export interface EmojiMetadataInfo {
77
+ id: string;
78
+ guildID: string;
79
+ name: string;
80
+ animated: boolean;
81
+ allowCloning: boolean;
82
+ }
83
+
84
+ /** Metadados de sticker (`GET /stickers/{id}/metadata`). */
85
+ export interface StickerMetadataInfo {
86
+ id: string;
87
+ guildID: string;
88
+ name: string;
89
+ animated: boolean;
90
+ allowCloning: boolean;
91
+ }
92
+
93
+ /** Referência de emoji aceita por `resolveEmoji` além do texto. */
94
+ export interface EmojiReference {
95
+ name: string;
96
+ id?: string;
97
+ animated?: boolean;
98
+ }
99
+
100
+ /** Introspecção da instância, do gateway e da aplicação do client. */
101
+ export interface InstanceResources {
102
+ describe(): Promise<InstanceInfo>;
103
+ gatewayInfo(): Promise<GatewayInfo>;
104
+ application(): Promise<ApplicationInfo>;
105
+ oauthApplications(): Promise<ApplicationInfo[]>;
106
+ emojiMetadata(emojiID: string): Promise<EmojiMetadataInfo>;
107
+ stickerMetadata(stickerID: string): Promise<StickerMetadataInfo>;
108
+ resolveEmoji(emoji: string | EmojiReference, guildID?: string): Promise<string>;
109
+ }
@@ -1,4 +1,5 @@
1
1
  import type { AttachmentSpec, BinaryAttachmentSpec } from './common.js';
2
+ import type { APIMessage } from '@fluxerjs/types';
2
3
  import type { MessageSearchInput, PinnedMessagesInput } from '../../domain/message-query.js';
3
4
  import type { ReceivedEmbed } from '../../domain/messages.js';
4
5
  export type { MessageSearchInput, PinnedMessagesInput } from '../../domain/message-query.js';
@@ -40,6 +41,8 @@ export interface MessagesResources {
40
41
  dm(userID: string, content: string): Promise<string>;
41
42
  forward(channelID: string, sourceChannelID: string, messageID: string): Promise<string>;
42
43
  search(input: MessageSearchInput): Promise<MessageSearchPage>;
44
+ searchAll(input: MessageSearchInput): Promise<MessageSearchPage>;
45
+ bulkFetch(requests: BulkFetchRequest[], input?: BulkFetchInput): Promise<MessageInfo[] | BulkFetchRawChannel[]>;
43
46
  sendBytes(channelID: string, files: BinaryAttachmentSpec[], content?: string): Promise<string>;
44
47
  sendAttachments(channelID: string, files: (AttachmentSpec | BinaryAttachmentSpec)[], content?: string): Promise<string>;
45
48
  }
@@ -62,3 +65,23 @@ export interface MessageSearchPage {
62
65
  total: number;
63
66
  cursor: string[] | null;
64
67
  }
68
+
69
+ /** Janela de mensagens de um canal dentro de um `bulkFetch`. */
70
+ export interface BulkFetchRequest {
71
+ channelID: string;
72
+ limit: number;
73
+ before?: string;
74
+ after?: string;
75
+ around?: string;
76
+ }
77
+
78
+ export interface BulkFetchInput {
79
+ /** `false` retorna o payload bruto da API; o padrão hidrata e normaliza. */
80
+ hydrate?: boolean;
81
+ }
82
+
83
+ /** Canal do retorno bruto de `bulkFetch` quando `hydrate` é `false`. */
84
+ export interface BulkFetchRawChannel {
85
+ channelID: string;
86
+ messages: APIMessage[];
87
+ }
@@ -0,0 +1,30 @@
1
+ import type { UserInfo } from './users.js';
2
+
3
+ /** Credenciais para verificação sudo (ações elevadas): senha e/ou MFA. */
4
+ export interface SudoVerification {
5
+ password?: string;
6
+ mfaMethod?: 'totp' | 'webauthn';
7
+ mfaCode?: string;
8
+ webauthnResponse?: Record<string, unknown>;
9
+ webauthnChallenge?: string;
10
+ }
11
+
12
+ /** Servidor parcial retornado por `GET /users/@me/guilds`. */
13
+ export interface SelfGuildInfo {
14
+ id: string;
15
+ name: string;
16
+ icon: string | null;
17
+ owner: boolean;
18
+ permissions: string | null;
19
+ features: string[];
20
+ memberCount: number | null;
21
+ onlineCount: number | null;
22
+ }
23
+
24
+ /** Operações sobre o usuário autenticado (`client.user`). */
25
+ export interface SelfResources {
26
+ fetch(): Promise<UserInfo>;
27
+ guilds(input?: { withCounts?: boolean }): Promise<SelfGuildInfo[]>;
28
+ leaveGuild(guildID: string, verification?: SudoVerification): Promise<void>;
29
+ deleteMyMessages(guildID: string, verification?: SudoVerification): Promise<void>;
30
+ }