@erinjs/core 1.0.0 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{core/LICENSE → LICENSE} +203 -203
- package/README.md +46 -0
- package/dist/index.d.mts +2084 -0
- package/dist/index.d.ts +2084 -0
- package/dist/index.js +3867 -0
- package/dist/index.mjs +3809 -0
- package/package.json +50 -15
- package/core/.changeset/README.md +0 -8
- package/core/.changeset/community-bootstrap-release.md +0 -17
- package/core/.changeset/config.json +0 -11
- package/core/.changeset/no-changelog.js +0 -16
- package/core/.changeset/pre.json +0 -17
- package/core/.editorconfig +0 -13
- package/core/.gitattributes +0 -2
- package/core/.github/CODE_OF_CONDUCT.md +0 -23
- package/core/.github/FUNDING.yml +0 -7
- package/core/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
- package/core/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/core/.github/PULL_REQUEST_TEMPLATE.md +0 -16
- package/core/.github/dependabot.yml +0 -16
- package/core/.github/workflows/autoapp.yml +0 -16
- package/core/.github/workflows/ci.yml +0 -187
- package/core/.github/workflows/codeql.yml +0 -30
- package/core/.github/workflows/deploy-docs.yml +0 -54
- package/core/.github/workflows/publish.yml +0 -43
- package/core/.lintstagedrc.json +0 -4
- package/core/.nvmrc +0 -1
- package/core/.prettierignore +0 -8
- package/core/.prettierrc +0 -11
- package/core/CONTRIBUTING.md +0 -70
- package/core/README.md +0 -61
- package/core/SECURITY.md +0 -21
- package/core/apps/docs/index.html +0 -28
- package/core/apps/docs/middleware.ts +0 -21
- package/core/apps/docs/package.json +0 -33
- package/core/apps/docs/public/@flux.png +0 -0
- package/core/apps/docs/public/docs/latest/guides.json +0 -1420
- package/core/apps/docs/public/docs/latest/main.json +0 -14981
- package/core/apps/docs/public/docs/latest/rag-index.json +0 -1
- package/core/apps/docs/public/docs/v1.0.5/guides.json +0 -226
- package/core/apps/docs/public/docs/v1.0.5/main.json +0 -7920
- package/core/apps/docs/public/docs/v1.0.6/guides.json +0 -226
- package/core/apps/docs/public/docs/v1.0.6/main.json +0 -7920
- package/core/apps/docs/public/docs/v1.0.7/guides.json +0 -259
- package/core/apps/docs/public/docs/v1.0.7/main.json +0 -8652
- package/core/apps/docs/public/docs/v1.0.8/guides.json +0 -313
- package/core/apps/docs/public/docs/v1.0.8/main.json +0 -9618
- package/core/apps/docs/public/docs/v1.0.9/guides.json +0 -319
- package/core/apps/docs/public/docs/v1.0.9/main.json +0 -10694
- package/core/apps/docs/public/docs/v1.1.0/guides.json +0 -589
- package/core/apps/docs/public/docs/v1.1.0/main.json +0 -12576
- package/core/apps/docs/public/docs/v1.1.2/guides.json +0 -650
- package/core/apps/docs/public/docs/v1.1.2/main.json +0 -13239
- package/core/apps/docs/public/docs/v1.1.3/guides.json +0 -650
- package/core/apps/docs/public/docs/v1.1.3/main.json +0 -13239
- package/core/apps/docs/public/docs/v1.1.4/guides.json +0 -708
- package/core/apps/docs/public/docs/v1.1.4/main.json +0 -13231
- package/core/apps/docs/public/docs/v1.1.5/guides.json +0 -1035
- package/core/apps/docs/public/docs/v1.1.5/main.json +0 -13838
- package/core/apps/docs/public/docs/v1.1.6/guides.json +0 -1041
- package/core/apps/docs/public/docs/v1.1.6/main.json +0 -14313
- package/core/apps/docs/public/docs/v1.1.8/guides.json +0 -1047
- package/core/apps/docs/public/docs/v1.1.8/main.json +0 -14421
- package/core/apps/docs/public/docs/v1.1.9/guides.json +0 -1047
- package/core/apps/docs/public/docs/v1.1.9/main.json +0 -14421
- package/core/apps/docs/public/docs/v1.2.0/guides.json +0 -1212
- package/core/apps/docs/public/docs/v1.2.0/main.json +0 -14663
- package/core/apps/docs/public/docs/v1.2.1/guides.json +0 -1293
- package/core/apps/docs/public/docs/v1.2.1/main.json +0 -14828
- package/core/apps/docs/public/docs/v1.2.2/guides.json +0 -1293
- package/core/apps/docs/public/docs/v1.2.2/main.json +0 -15025
- package/core/apps/docs/public/docs/v1.2.3/guides.json +0 -1420
- package/core/apps/docs/public/docs/v1.2.3/main.json +0 -14954
- package/core/apps/docs/public/docs/v1.2.4/guides.json +0 -1420
- package/core/apps/docs/public/docs/v1.2.4/main.json +0 -14981
- package/core/apps/docs/public/docs/versions.json +0 -24
- package/core/apps/docs/public/flux.png +0 -0
- package/core/apps/docs/public/locales/en.json +0 -50
- package/core/apps/docs/public/locales/guides-en.json +0 -512
- package/core/apps/docs/public/robots.txt +0 -4
- package/core/apps/docs/public/sitemap.xml +0 -33
- package/core/apps/docs/src/App.vue +0 -538
- package/core/apps/docs/src/components/ApiCategorySection.vue +0 -42
- package/core/apps/docs/src/components/ApiDiscordCompat.vue +0 -65
- package/core/apps/docs/src/components/ApiEndpointCard.vue +0 -313
- package/core/apps/docs/src/components/ApiSchemaBlock.vue +0 -131
- package/core/apps/docs/src/components/CodeBlock.vue +0 -177
- package/core/apps/docs/src/components/CommunityCallout.vue +0 -90
- package/core/apps/docs/src/components/ConstructorSection.vue +0 -82
- package/core/apps/docs/src/components/DocDescription.vue +0 -40
- package/core/apps/docs/src/components/FluxerLogo.vue +0 -3
- package/core/apps/docs/src/components/Footer.vue +0 -106
- package/core/apps/docs/src/components/GuideCodeBlock.vue +0 -102
- package/core/apps/docs/src/components/GuideDiscordCompat.vue +0 -77
- package/core/apps/docs/src/components/GuideDiscordCompatCallout.vue +0 -83
- package/core/apps/docs/src/components/GuideTable.vue +0 -77
- package/core/apps/docs/src/components/GuideTip.vue +0 -38
- package/core/apps/docs/src/components/MethodsSection.vue +0 -195
- package/core/apps/docs/src/components/ParamsTable.vue +0 -70
- package/core/apps/docs/src/components/PropertiesSection.vue +0 -143
- package/core/apps/docs/src/components/SearchBar.vue +0 -76
- package/core/apps/docs/src/components/SearchModal.vue +0 -361
- package/core/apps/docs/src/components/SidebarNav.vue +0 -225
- package/core/apps/docs/src/components/SponsorBanner.vue +0 -153
- package/core/apps/docs/src/components/TypeSignature.vue +0 -187
- package/core/apps/docs/src/components/VersionPicker.vue +0 -191
- package/core/apps/docs/src/composables/useSearchIndex.ts +0 -144
- package/core/apps/docs/src/composables/useVersionedPath.ts +0 -20
- package/core/apps/docs/src/data/apiEndpoints.ts +0 -1073
- package/core/apps/docs/src/data/changelog.ts +0 -717
- package/core/apps/docs/src/data/guides.ts +0 -2362
- package/core/apps/docs/src/env.d.ts +0 -7
- package/core/apps/docs/src/locales/guides-en.json +0 -512
- package/core/apps/docs/src/main.ts +0 -27
- package/core/apps/docs/src/pages/ApiReferenceLayout.vue +0 -175
- package/core/apps/docs/src/pages/ApiReferencePage.vue +0 -128
- package/core/apps/docs/src/pages/Changelog.vue +0 -288
- package/core/apps/docs/src/pages/ClassPage.vue +0 -319
- package/core/apps/docs/src/pages/ClassesList.vue +0 -100
- package/core/apps/docs/src/pages/DocsLayout.vue +0 -127
- package/core/apps/docs/src/pages/GuidePage.vue +0 -279
- package/core/apps/docs/src/pages/GuidesIndex.vue +0 -166
- package/core/apps/docs/src/pages/GuidesLayout.vue +0 -245
- package/core/apps/docs/src/pages/Home.vue +0 -125
- package/core/apps/docs/src/pages/NotFound.vue +0 -57
- package/core/apps/docs/src/pages/TypedefPage.vue +0 -230
- package/core/apps/docs/src/pages/TypedefsList.vue +0 -168
- package/core/apps/docs/src/pages/VersionLayout.vue +0 -15
- package/core/apps/docs/src/router.ts +0 -73
- package/core/apps/docs/src/stores/docs.ts +0 -54
- package/core/apps/docs/src/stores/guides.ts +0 -53
- package/core/apps/docs/src/stores/version.ts +0 -67
- package/core/apps/docs/src/styles/main.css +0 -278
- package/core/apps/docs/src/styles/prism.css +0 -95
- package/core/apps/docs/src/types/doc-schema.ts +0 -112
- package/core/apps/docs/tsconfig.json +0 -17
- package/core/apps/docs/tsconfig.node.json +0 -10
- package/core/apps/docs/vite.config.d.ts +0 -2
- package/core/apps/docs/vite.config.js +0 -26
- package/core/apps/docs/vite.config.ts +0 -28
- package/core/apps/docs-vitepress/.vitepress/config.ts +0 -141
- package/core/apps/docs-vitepress/api-data/latest/main.json +0 -15035
- package/core/apps/docs-vitepress/api-data/v1.2.4/main.json +0 -15035
- package/core/apps/docs-vitepress/api-data/versions.json +0 -6
- package/core/apps/docs-vitepress/index.md +0 -15
- package/core/apps/docs-vitepress/package-lock.json +0 -2924
- package/core/apps/docs-vitepress/package.json +0 -20
- package/core/apps/docs-vitepress/public/CNAME +0 -1
- package/core/apps/docs-vitepress/scripts/generate-api.ts +0 -243
- package/core/apps/docs-vitepress/scripts/migrate-guides.ts +0 -129
- package/core/apps/docs-vitepress/tsconfig.json +0 -11
- package/core/apps/docs-vitepress/v/latest/guides/attachments-by-url.md +0 -57
- package/core/apps/docs-vitepress/v/latest/guides/attachments.md +0 -62
- package/core/apps/docs-vitepress/v/latest/guides/basic-bot.md +0 -49
- package/core/apps/docs-vitepress/v/latest/guides/channels.md +0 -180
- package/core/apps/docs-vitepress/v/latest/guides/deprecated-apis.md +0 -58
- package/core/apps/docs-vitepress/v/latest/guides/discord-js-compatibility.md +0 -42
- package/core/apps/docs-vitepress/v/latest/guides/editing-embeds.md +0 -65
- package/core/apps/docs-vitepress/v/latest/guides/embed-media.md +0 -87
- package/core/apps/docs-vitepress/v/latest/guides/embeds.md +0 -166
- package/core/apps/docs-vitepress/v/latest/guides/emojis.md +0 -77
- package/core/apps/docs-vitepress/v/latest/guides/events.md +0 -202
- package/core/apps/docs-vitepress/v/latest/guides/gifs.md +0 -47
- package/core/apps/docs-vitepress/v/latest/guides/installation.md +0 -10
- package/core/apps/docs-vitepress/v/latest/guides/moderation.md +0 -89
- package/core/apps/docs-vitepress/v/latest/guides/permissions.md +0 -130
- package/core/apps/docs-vitepress/v/latest/guides/prefix-commands.md +0 -41
- package/core/apps/docs-vitepress/v/latest/guides/profile-urls.md +0 -58
- package/core/apps/docs-vitepress/v/latest/guides/reactions.md +0 -69
- package/core/apps/docs-vitepress/v/latest/guides/roles.md +0 -130
- package/core/apps/docs-vitepress/v/latest/guides/sending-without-reply.md +0 -172
- package/core/apps/docs-vitepress/v/latest/guides/voice.md +0 -109
- package/core/apps/docs-vitepress/v/latest/guides/wait-for-guilds.md +0 -37
- package/core/apps/docs-vitepress/v/latest/guides/webhook-attachments-embeds.md +0 -73
- package/core/apps/docs-vitepress/v/latest/guides/webhooks.md +0 -131
- package/core/eslint.config.js +0 -80
- package/core/examples/.env.example +0 -22
- package/core/examples/README.md +0 -68
- package/core/examples/first-steps-bot.js +0 -118
- package/core/examples/minimal-bot.js +0 -17
- package/core/examples/moderation-bot.js +0 -209
- package/core/examples/package.json +0 -14
- package/core/examples/ping-bot.js +0 -1146
- package/core/examples/reaction-bot.js +0 -70
- package/core/examples/reaction-roles-bot.js +0 -140
- package/core/examples/webhook-bot.js +0 -239
- package/core/flux.png +0 -0
- package/core/package.json +0 -78
- package/core/packages/builders/package.json +0 -51
- package/core/packages/builders/src/index.ts +0 -13
- package/core/packages/builders/src/messages/AttachmentBuilder.test.ts +0 -79
- package/core/packages/builders/src/messages/AttachmentBuilder.ts +0 -69
- package/core/packages/builders/src/messages/EmbedBuilder.test.ts +0 -266
- package/core/packages/builders/src/messages/EmbedBuilder.ts +0 -239
- package/core/packages/builders/src/messages/MessagePayload.test.ts +0 -118
- package/core/packages/builders/src/messages/MessagePayload.ts +0 -122
- package/core/packages/builders/tsconfig.json +0 -9
- package/core/packages/builders/tsup.config.ts +0 -9
- package/core/packages/builders/vitest.config.ts +0 -9
- package/core/packages/collection/package.json +0 -47
- package/core/packages/collection/src/Collection.test.ts +0 -232
- package/core/packages/collection/src/Collection.ts +0 -196
- package/core/packages/collection/src/index.ts +0 -1
- package/core/packages/collection/tsconfig.json +0 -9
- package/core/packages/collection/tsup.config.ts +0 -9
- package/core/packages/collection/vitest.config.ts +0 -9
- package/core/packages/docgen/package.json +0 -26
- package/core/packages/docgen/src/extract.ts +0 -262
- package/core/packages/docgen/src/formatType.ts +0 -24
- package/core/packages/docgen/src/index.ts +0 -103
- package/core/packages/docgen/src/schema.ts +0 -100
- package/core/packages/docgen/src/visitor.ts +0 -147
- package/core/packages/docgen/tsconfig.json +0 -9
- package/core/packages/docgen/tsup.config.ts +0 -9
- package/core/packages/fluxer-core/README.md +0 -26
- package/core/packages/fluxer-core/package.json +0 -60
- package/core/packages/fluxer-core/src/client/ChannelManager.ts +0 -143
- package/core/packages/fluxer-core/src/client/Client.gateway.test.ts +0 -84
- package/core/packages/fluxer-core/src/client/Client.resolveEmoji.test.ts +0 -45
- package/core/packages/fluxer-core/src/client/Client.ts +0 -558
- package/core/packages/fluxer-core/src/client/ClientUser.ts +0 -40
- package/core/packages/fluxer-core/src/client/EventHandlerRegistry.ts +0 -469
- package/core/packages/fluxer-core/src/client/GuildManager.ts +0 -79
- package/core/packages/fluxer-core/src/client/GuildMemberManager.ts +0 -91
- package/core/packages/fluxer-core/src/client/MessageManager.ts +0 -58
- package/core/packages/fluxer-core/src/client/UsersManager.ts +0 -122
- package/core/packages/fluxer-core/src/errors/ErrorCodes.test.ts +0 -19
- package/core/packages/fluxer-core/src/errors/ErrorCodes.ts +0 -12
- package/core/packages/fluxer-core/src/errors/FluxerError.test.ts +0 -32
- package/core/packages/fluxer-core/src/errors/FluxerError.ts +0 -15
- package/core/packages/fluxer-core/src/index.ts +0 -85
- package/core/packages/fluxer-core/src/structures/Base.ts +0 -7
- package/core/packages/fluxer-core/src/structures/Channel.ts +0 -508
- package/core/packages/fluxer-core/src/structures/Guild.test.ts +0 -189
- package/core/packages/fluxer-core/src/structures/Guild.ts +0 -734
- package/core/packages/fluxer-core/src/structures/GuildBan.ts +0 -35
- package/core/packages/fluxer-core/src/structures/GuildEmoji.ts +0 -57
- package/core/packages/fluxer-core/src/structures/GuildMember.test.ts +0 -203
- package/core/packages/fluxer-core/src/structures/GuildMember.ts +0 -213
- package/core/packages/fluxer-core/src/structures/GuildMemberRoleManager.ts +0 -121
- package/core/packages/fluxer-core/src/structures/GuildSticker.ts +0 -56
- package/core/packages/fluxer-core/src/structures/Invite.test.ts +0 -103
- package/core/packages/fluxer-core/src/structures/Invite.ts +0 -121
- package/core/packages/fluxer-core/src/structures/Message.test.ts +0 -109
- package/core/packages/fluxer-core/src/structures/Message.ts +0 -397
- package/core/packages/fluxer-core/src/structures/MessageReaction.ts +0 -72
- package/core/packages/fluxer-core/src/structures/PartialMessage.ts +0 -12
- package/core/packages/fluxer-core/src/structures/Role.test.ts +0 -77
- package/core/packages/fluxer-core/src/structures/Role.ts +0 -112
- package/core/packages/fluxer-core/src/structures/User.test.ts +0 -110
- package/core/packages/fluxer-core/src/structures/User.ts +0 -109
- package/core/packages/fluxer-core/src/structures/Webhook.test.ts +0 -109
- package/core/packages/fluxer-core/src/structures/Webhook.ts +0 -258
- package/core/packages/fluxer-core/src/util/Constants.test.ts +0 -16
- package/core/packages/fluxer-core/src/util/Constants.ts +0 -7
- package/core/packages/fluxer-core/src/util/Events.ts +0 -46
- package/core/packages/fluxer-core/src/util/MessageCollector.ts +0 -87
- package/core/packages/fluxer-core/src/util/Options.ts +0 -33
- package/core/packages/fluxer-core/src/util/ReactionCollector.ts +0 -116
- package/core/packages/fluxer-core/src/util/cdn.test.ts +0 -108
- package/core/packages/fluxer-core/src/util/cdn.ts +0 -130
- package/core/packages/fluxer-core/src/util/guildUtils.ts +0 -33
- package/core/packages/fluxer-core/src/util/messageUtils.test.ts +0 -74
- package/core/packages/fluxer-core/src/util/messageUtils.ts +0 -119
- package/core/packages/fluxer-core/src/util/permissions.test.ts +0 -95
- package/core/packages/fluxer-core/src/util/permissions.ts +0 -43
- package/core/packages/fluxer-core/tsconfig.json +0 -9
- package/core/packages/fluxer-core/tsup.config.ts +0 -9
- package/core/packages/fluxer-core/vitest.config.ts +0 -9
- package/core/packages/rest/package.json +0 -52
- package/core/packages/rest/src/REST.test.ts +0 -64
- package/core/packages/rest/src/REST.ts +0 -90
- package/core/packages/rest/src/RateLimitManager.test.ts +0 -71
- package/core/packages/rest/src/RateLimitManager.ts +0 -60
- package/core/packages/rest/src/RequestManager.test.ts +0 -87
- package/core/packages/rest/src/RequestManager.ts +0 -172
- package/core/packages/rest/src/errors/FluxerAPIError.test.ts +0 -57
- package/core/packages/rest/src/errors/FluxerAPIError.ts +0 -21
- package/core/packages/rest/src/errors/HTTPError.test.ts +0 -55
- package/core/packages/rest/src/errors/HTTPError.ts +0 -25
- package/core/packages/rest/src/errors/RateLimitError.test.ts +0 -41
- package/core/packages/rest/src/errors/RateLimitError.ts +0 -15
- package/core/packages/rest/src/errors/index.ts +0 -3
- package/core/packages/rest/src/index.ts +0 -6
- package/core/packages/rest/src/utils/constants.test.ts +0 -31
- package/core/packages/rest/src/utils/constants.ts +0 -5
- package/core/packages/rest/src/utils/files.test.ts +0 -37
- package/core/packages/rest/src/utils/files.ts +0 -75
- package/core/packages/rest/tsconfig.json +0 -9
- package/core/packages/rest/tsup.config.ts +0 -9
- package/core/packages/rest/vitest.config.ts +0 -9
- package/core/packages/types/package.json +0 -46
- package/core/packages/types/src/api/ban.ts +0 -8
- package/core/packages/types/src/api/channel.ts +0 -65
- package/core/packages/types/src/api/embed.ts +0 -82
- package/core/packages/types/src/api/emoji.ts +0 -12
- package/core/packages/types/src/api/errors.ts +0 -68
- package/core/packages/types/src/api/gateway.ts +0 -14
- package/core/packages/types/src/api/guild.ts +0 -123
- package/core/packages/types/src/api/index.ts +0 -15
- package/core/packages/types/src/api/instance.ts +0 -32
- package/core/packages/types/src/api/interaction.ts +0 -26
- package/core/packages/types/src/api/invite.ts +0 -28
- package/core/packages/types/src/api/message.ts +0 -140
- package/core/packages/types/src/api/role.ts +0 -41
- package/core/packages/types/src/api/sticker.ts +0 -14
- package/core/packages/types/src/api/user.ts +0 -79
- package/core/packages/types/src/api/webhook.ts +0 -41
- package/core/packages/types/src/common/index.ts +0 -1
- package/core/packages/types/src/common/snowflake.test.ts +0 -9
- package/core/packages/types/src/common/snowflake.ts +0 -8
- package/core/packages/types/src/gateway/events.ts +0 -189
- package/core/packages/types/src/gateway/index.ts +0 -3
- package/core/packages/types/src/gateway/opcodes.ts +0 -17
- package/core/packages/types/src/gateway/payloads.ts +0 -481
- package/core/packages/types/src/index.ts +0 -4
- package/core/packages/types/src/rest/index.ts +0 -1
- package/core/packages/types/src/rest/routes.test.ts +0 -169
- package/core/packages/types/src/rest/routes.ts +0 -109
- package/core/packages/types/tsconfig.json +0 -9
- package/core/packages/types/tsup.config.ts +0 -9
- package/core/packages/types/vitest.config.ts +0 -9
- package/core/packages/util/package.json +0 -51
- package/core/packages/util/src/BitField.test.ts +0 -96
- package/core/packages/util/src/BitField.ts +0 -105
- package/core/packages/util/src/MessageFlagsBitField.test.ts +0 -42
- package/core/packages/util/src/MessageFlagsBitField.ts +0 -20
- package/core/packages/util/src/PermissionsBitField.test.ts +0 -79
- package/core/packages/util/src/PermissionsBitField.ts +0 -97
- package/core/packages/util/src/SnowflakeUtil.test.ts +0 -69
- package/core/packages/util/src/SnowflakeUtil.ts +0 -65
- package/core/packages/util/src/UserFlagsBitField.test.ts +0 -39
- package/core/packages/util/src/UserFlagsBitField.ts +0 -48
- package/core/packages/util/src/deprecation.test.ts +0 -44
- package/core/packages/util/src/deprecation.ts +0 -28
- package/core/packages/util/src/emojiShortcodes.generated.ts +0 -5
- package/core/packages/util/src/emojiShortcodes.test.ts +0 -41
- package/core/packages/util/src/emojiShortcodes.ts +0 -22
- package/core/packages/util/src/formatters.test.ts +0 -65
- package/core/packages/util/src/formatters.ts +0 -35
- package/core/packages/util/src/index.ts +0 -34
- package/core/packages/util/src/resolvers.test.ts +0 -198
- package/core/packages/util/src/resolvers.ts +0 -127
- package/core/packages/util/src/tenorUtils.test.ts +0 -75
- package/core/packages/util/src/tenorUtils.ts +0 -86
- package/core/packages/util/tsconfig.json +0 -9
- package/core/packages/util/tsup.config.ts +0 -9
- package/core/packages/util/vitest.config.ts +0 -9
- package/core/packages/voice/README.md +0 -42
- package/core/packages/voice/package.json +0 -67
- package/core/packages/voice/src/LiveKitRtcConnection.receive.test.ts +0 -24
- package/core/packages/voice/src/LiveKitRtcConnection.ts +0 -1767
- package/core/packages/voice/src/VoiceConnection.ts +0 -413
- package/core/packages/voice/src/VoiceManager.receive.test.ts +0 -61
- package/core/packages/voice/src/VoiceManager.test.ts +0 -44
- package/core/packages/voice/src/VoiceManager.ts +0 -503
- package/core/packages/voice/src/exports.test.ts +0 -38
- package/core/packages/voice/src/index.ts +0 -51
- package/core/packages/voice/src/livekit.test.ts +0 -48
- package/core/packages/voice/src/livekit.ts +0 -33
- package/core/packages/voice/src/mp4box.d.ts +0 -32
- package/core/packages/voice/src/opusUtils.test.ts +0 -29
- package/core/packages/voice/src/opusUtils.ts +0 -86
- package/core/packages/voice/src/streamPreviewPlaceholder.test.ts +0 -16
- package/core/packages/voice/src/streamPreviewPlaceholder.ts +0 -8
- package/core/packages/voice/src/ws.d.ts +0 -1
- package/core/packages/voice/tsconfig.json +0 -5
- package/core/packages/voice/tsup.config.ts +0 -10
- package/core/packages/voice/vitest.config.ts +0 -9
- package/core/packages/ws/package.json +0 -52
- package/core/packages/ws/src/WebSocketManager.ts +0 -130
- package/core/packages/ws/src/WebSocketShard.ts +0 -296
- package/core/packages/ws/src/index.ts +0 -12
- package/core/packages/ws/src/utils/constants.test.ts +0 -46
- package/core/packages/ws/src/utils/constants.ts +0 -22
- package/core/packages/ws/src/utils/getWebSocket.ts +0 -55
- package/core/packages/ws/src/ws.d.ts +0 -10
- package/core/packages/ws/tsconfig.json +0 -9
- package/core/packages/ws/tsup.config.ts +0 -9
- package/core/pnpm-lock.yaml +0 -7033
- package/core/pnpm-workspace.yaml +0 -4
- package/core/scripts/generate-ai-rag.ts +0 -240
- package/core/scripts/generate-docs.ts +0 -143
- package/core/scripts/generate-emoji-shortcodes.ts +0 -58
- package/core/scripts/generate-types.ts +0 -6
- package/core/scripts/publish-ordered.js +0 -63
- package/core/scripts/test-cjs-require.mjs +0 -43
- package/core/scripts/test-esm-imports.mjs +0 -42
- package/core/scripts/test-package-exports.mjs +0 -98
- package/core/scripts/test-smoke.mjs +0 -103
- package/core/tsconfig.json +0 -18
- package/core/turbo.json +0 -30
- package/core/vitest.config.ts +0 -17
- package/core/wrangler.jsonc +0 -9
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3809 @@
|
|
|
1
|
+
// src/client/Client.ts
|
|
2
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
3
|
+
import { REST } from "@erinjs/rest";
|
|
4
|
+
import { WebSocketManager } from "@erinjs/ws";
|
|
5
|
+
import {
|
|
6
|
+
Routes as Routes20
|
|
7
|
+
} from "@erinjs/types";
|
|
8
|
+
|
|
9
|
+
// src/client/ChannelManager.ts
|
|
10
|
+
import { Collection as Collection4 } from "@erinjs/collection";
|
|
11
|
+
import { Routes as Routes6 } from "@erinjs/types";
|
|
12
|
+
import { emitDeprecationWarning as emitDeprecationWarning2 } from "@erinjs/util";
|
|
13
|
+
import { FluxerAPIError as FluxerAPIError2, RateLimitError as RateLimitError2 } from "@erinjs/rest";
|
|
14
|
+
|
|
15
|
+
// src/errors/FluxerError.ts
|
|
16
|
+
var FluxerError = class _FluxerError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, options?.cause ? { cause: options.cause } : void 0);
|
|
20
|
+
this.name = "FluxerError";
|
|
21
|
+
this.code = options?.code;
|
|
22
|
+
Object.setPrototypeOf(this, _FluxerError.prototype);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/errors/ErrorCodes.ts
|
|
27
|
+
var ErrorCodes = {
|
|
28
|
+
ClientNotReady: "CLIENT_NOT_READY",
|
|
29
|
+
InvalidToken: "INVALID_TOKEN",
|
|
30
|
+
AlreadyLoggedIn: "ALREADY_LOGGED_IN",
|
|
31
|
+
ChannelNotFound: "CHANNEL_NOT_FOUND",
|
|
32
|
+
MessageNotFound: "MESSAGE_NOT_FOUND",
|
|
33
|
+
GuildNotFound: "GUILD_NOT_FOUND",
|
|
34
|
+
MemberNotFound: "MEMBER_NOT_FOUND",
|
|
35
|
+
RoleNotFound: "ROLE_NOT_FOUND",
|
|
36
|
+
EmojiNotInGuild: "EMOJI_NOT_IN_GUILD",
|
|
37
|
+
EmojiNotFound: "EMOJI_NOT_FOUND"
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/util/messageUtils.ts
|
|
41
|
+
import { EmbedBuilder } from "@erinjs/builders";
|
|
42
|
+
var FILE_FETCH_TIMEOUT_MS = 3e4;
|
|
43
|
+
async function resolveMessageFiles(files) {
|
|
44
|
+
const result = [];
|
|
45
|
+
for (let i = 0; i < files.length; i++) {
|
|
46
|
+
const f = files[i];
|
|
47
|
+
const filename = f.filename ?? f.name;
|
|
48
|
+
if ("url" in f && f.url) {
|
|
49
|
+
if (!URL.canParse(f.url)) {
|
|
50
|
+
throw new Error(`Invalid file URL at index ${i}: ${f.url}`);
|
|
51
|
+
}
|
|
52
|
+
const res = await fetch(f.url, {
|
|
53
|
+
signal: AbortSignal.timeout(FILE_FETCH_TIMEOUT_MS)
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new Error(`Failed to fetch file from ${f.url}: ${res.status} ${res.statusText}`);
|
|
57
|
+
}
|
|
58
|
+
const data = await res.arrayBuffer();
|
|
59
|
+
result.push({ name: f.name, data, filename });
|
|
60
|
+
} else if ("data" in f && f.data != null) {
|
|
61
|
+
result.push({ name: f.name, data: f.data, filename });
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(`File at index ${i} must have either "data" or "url"`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
function buildSendBody(options) {
|
|
69
|
+
const body = typeof options === "string" ? { content: options } : options;
|
|
70
|
+
const result = {};
|
|
71
|
+
if (body.content !== void 0) result.content = body.content;
|
|
72
|
+
if (body.embeds?.length) {
|
|
73
|
+
result.embeds = body.embeds.map((e) => e instanceof EmbedBuilder ? e.toJSON() : e);
|
|
74
|
+
}
|
|
75
|
+
if (body.files?.length && body.attachments) {
|
|
76
|
+
result.attachments = body.attachments.map((a) => ({
|
|
77
|
+
id: a.id,
|
|
78
|
+
filename: a.filename,
|
|
79
|
+
...a.title != null && { title: a.title },
|
|
80
|
+
...a.description != null && { description: a.description },
|
|
81
|
+
...a.flags != null && { flags: a.flags }
|
|
82
|
+
}));
|
|
83
|
+
} else if (body.files?.length) {
|
|
84
|
+
result.attachments = body.files.map((f, i) => ({
|
|
85
|
+
id: i,
|
|
86
|
+
filename: f.filename ?? f.name
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
if (body.flags !== void 0) result.flags = body.flags;
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/client/MessageManager.ts
|
|
94
|
+
import { Routes as Routes2 } from "@erinjs/types";
|
|
95
|
+
import { FluxerAPIError, RateLimitError } from "@erinjs/rest";
|
|
96
|
+
|
|
97
|
+
// src/structures/Base.ts
|
|
98
|
+
var Base = class {
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/structures/Message.ts
|
|
102
|
+
import { Collection as Collection2 } from "@erinjs/collection";
|
|
103
|
+
import { MessageType, MessageFlags, Routes } from "@erinjs/types";
|
|
104
|
+
import { EmbedBuilder as EmbedBuilder2 } from "@erinjs/builders";
|
|
105
|
+
|
|
106
|
+
// src/util/ReactionCollector.ts
|
|
107
|
+
import { EventEmitter } from "events";
|
|
108
|
+
import { Collection } from "@erinjs/collection";
|
|
109
|
+
|
|
110
|
+
// src/util/Events.ts
|
|
111
|
+
var Events = {
|
|
112
|
+
Ready: "ready",
|
|
113
|
+
MessageCreate: "messageCreate",
|
|
114
|
+
MessageUpdate: "messageUpdate",
|
|
115
|
+
MessageDelete: "messageDelete",
|
|
116
|
+
MessageDeleteBulk: "messageDeleteBulk",
|
|
117
|
+
MessageReactionAdd: "messageReactionAdd",
|
|
118
|
+
MessageReactionRemove: "messageReactionRemove",
|
|
119
|
+
MessageReactionRemoveAll: "messageReactionRemoveAll",
|
|
120
|
+
MessageReactionRemoveEmoji: "messageReactionRemoveEmoji",
|
|
121
|
+
InteractionCreate: "interactionCreate",
|
|
122
|
+
GuildCreate: "guildCreate",
|
|
123
|
+
GuildUpdate: "guildUpdate",
|
|
124
|
+
GuildDelete: "guildDelete",
|
|
125
|
+
GuildBanAdd: "guildBanAdd",
|
|
126
|
+
GuildBanRemove: "guildBanRemove",
|
|
127
|
+
GuildEmojisUpdate: "guildEmojisUpdate",
|
|
128
|
+
GuildStickersUpdate: "guildStickersUpdate",
|
|
129
|
+
GuildIntegrationsUpdate: "guildIntegrationsUpdate",
|
|
130
|
+
GuildMemberAdd: "guildMemberAdd",
|
|
131
|
+
GuildMemberUpdate: "guildMemberUpdate",
|
|
132
|
+
GuildMemberRemove: "guildMemberRemove",
|
|
133
|
+
GuildMembersChunk: "guildMembersChunk",
|
|
134
|
+
GuildRoleCreate: "guildRoleCreate",
|
|
135
|
+
GuildRoleUpdate: "guildRoleUpdate",
|
|
136
|
+
GuildRoleDelete: "guildRoleDelete",
|
|
137
|
+
GuildScheduledEventCreate: "guildScheduledEventCreate",
|
|
138
|
+
GuildScheduledEventUpdate: "guildScheduledEventUpdate",
|
|
139
|
+
GuildScheduledEventDelete: "guildScheduledEventDelete",
|
|
140
|
+
ChannelCreate: "channelCreate",
|
|
141
|
+
ChannelUpdate: "channelUpdate",
|
|
142
|
+
ChannelDelete: "channelDelete",
|
|
143
|
+
ChannelPinsUpdate: "channelPinsUpdate",
|
|
144
|
+
InviteCreate: "inviteCreate",
|
|
145
|
+
InviteDelete: "inviteDelete",
|
|
146
|
+
TypingStart: "typingStart",
|
|
147
|
+
UserUpdate: "userUpdate",
|
|
148
|
+
PresenceUpdate: "presenceUpdate",
|
|
149
|
+
VoiceStateUpdate: "voiceStateUpdate",
|
|
150
|
+
VoiceServerUpdate: "voiceServerUpdate",
|
|
151
|
+
VoiceStatesSync: "voiceStatesSync",
|
|
152
|
+
WebhooksUpdate: "webhooksUpdate",
|
|
153
|
+
Resumed: "resumed",
|
|
154
|
+
Error: "error",
|
|
155
|
+
Debug: "debug"
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/util/ReactionCollector.ts
|
|
159
|
+
var ReactionCollector = class extends EventEmitter {
|
|
160
|
+
client;
|
|
161
|
+
messageId;
|
|
162
|
+
channelId;
|
|
163
|
+
options;
|
|
164
|
+
collected = new Collection();
|
|
165
|
+
_timeout = null;
|
|
166
|
+
_ended = false;
|
|
167
|
+
_listener;
|
|
168
|
+
constructor(client, messageId, channelId, options = {}) {
|
|
169
|
+
super();
|
|
170
|
+
this.client = client;
|
|
171
|
+
this.messageId = messageId;
|
|
172
|
+
this.channelId = channelId;
|
|
173
|
+
this.options = {
|
|
174
|
+
filter: options.filter ?? (() => true),
|
|
175
|
+
time: options.time ?? 0,
|
|
176
|
+
max: options.max ?? 0
|
|
177
|
+
};
|
|
178
|
+
this._listener = (reaction, user, _msgId, chId, _emoji, userId) => {
|
|
179
|
+
if (this._ended || reaction.messageId !== this.messageId || chId !== this.channelId) return;
|
|
180
|
+
if (!this.options.filter(reaction, user)) return;
|
|
181
|
+
const key = `${userId}:${reaction.emoji.id ?? reaction.emoji.name}`;
|
|
182
|
+
this.collected.set(key, { reaction, user });
|
|
183
|
+
this.emit("collect", reaction, user);
|
|
184
|
+
if (this.options.max > 0 && this.collected.size >= this.options.max) {
|
|
185
|
+
this.stop("limit");
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
this.client.on(Events.MessageReactionAdd, this._listener);
|
|
189
|
+
if (this.options.time > 0) {
|
|
190
|
+
this._timeout = setTimeout(() => this.stop("time"), this.options.time);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
stop(reason = "user") {
|
|
194
|
+
if (this._ended) return;
|
|
195
|
+
this._ended = true;
|
|
196
|
+
this.client.off(Events.MessageReactionAdd, this._listener);
|
|
197
|
+
if (this._timeout) {
|
|
198
|
+
clearTimeout(this._timeout);
|
|
199
|
+
this._timeout = null;
|
|
200
|
+
}
|
|
201
|
+
this.emit("end", this.collected, reason);
|
|
202
|
+
}
|
|
203
|
+
on(event, listener) {
|
|
204
|
+
return super.on(event, listener);
|
|
205
|
+
}
|
|
206
|
+
emit(event, ...args) {
|
|
207
|
+
return super.emit(event, ...args);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/structures/Message.ts
|
|
212
|
+
var Message = class _Message extends Base {
|
|
213
|
+
client;
|
|
214
|
+
id;
|
|
215
|
+
channelId;
|
|
216
|
+
guildId;
|
|
217
|
+
author;
|
|
218
|
+
content;
|
|
219
|
+
createdAt;
|
|
220
|
+
editedAt;
|
|
221
|
+
pinned;
|
|
222
|
+
attachments;
|
|
223
|
+
type;
|
|
224
|
+
flags;
|
|
225
|
+
mentionEveryone;
|
|
226
|
+
tts;
|
|
227
|
+
embeds;
|
|
228
|
+
stickers;
|
|
229
|
+
reactions;
|
|
230
|
+
messageReference;
|
|
231
|
+
messageSnapshots;
|
|
232
|
+
call;
|
|
233
|
+
referencedMessage;
|
|
234
|
+
/** Webhook ID if this message was sent via webhook. Null otherwise. */
|
|
235
|
+
webhookId;
|
|
236
|
+
/** Users mentioned in this message. */
|
|
237
|
+
mentions;
|
|
238
|
+
/** Role IDs mentioned in this message. */
|
|
239
|
+
mentionRoles;
|
|
240
|
+
/** Client-side nonce for acknowledgment. Null if not provided. */
|
|
241
|
+
nonce;
|
|
242
|
+
/**
|
|
243
|
+
* Channel where this message was sent. Resolved from cache; null if not cached.
|
|
244
|
+
* Messages can only exist in text-based channels (text, DM, announcement), so this always has send() when non-null.
|
|
245
|
+
*/
|
|
246
|
+
get channel() {
|
|
247
|
+
return this.client.channels.get(this.channelId) ?? null;
|
|
248
|
+
}
|
|
249
|
+
/** Guild where this message was sent. Resolved from cache; null for DMs or if not cached. */
|
|
250
|
+
get guild() {
|
|
251
|
+
return this.guildId ? this.client.guilds.get(this.guildId) ?? null : null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Resolve the channel (from cache or API). Use when you need the channel and it may not be cached.
|
|
255
|
+
* @returns The channel
|
|
256
|
+
* @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
|
|
257
|
+
*/
|
|
258
|
+
async resolveChannel() {
|
|
259
|
+
return this.client.channels.resolve(this.channelId);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Resolve the guild (from cache or API). Returns null for DMs.
|
|
263
|
+
* @returns The guild, or null if this is a DM or guild not found
|
|
264
|
+
*/
|
|
265
|
+
async resolveGuild() {
|
|
266
|
+
return this.guildId ? this.client.guilds.resolve(this.guildId) : null;
|
|
267
|
+
}
|
|
268
|
+
/** @param data - API message from POST/PATCH /channels/{id}/messages or gateway MESSAGE_CREATE */
|
|
269
|
+
constructor(client, data) {
|
|
270
|
+
super();
|
|
271
|
+
this.client = client;
|
|
272
|
+
this.id = data.id;
|
|
273
|
+
this.channelId = data.channel_id;
|
|
274
|
+
this.guildId = data.guild_id ?? null;
|
|
275
|
+
this.author = client.getOrCreateUser(data.author);
|
|
276
|
+
this.content = data.content;
|
|
277
|
+
this.createdAt = new Date(data.timestamp);
|
|
278
|
+
this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
|
|
279
|
+
this.pinned = data.pinned;
|
|
280
|
+
this.attachments = new Collection2();
|
|
281
|
+
for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
|
|
282
|
+
this.type = data.type ?? MessageType.Default;
|
|
283
|
+
this.flags = data.flags ?? 0;
|
|
284
|
+
this.mentionEveryone = data.mention_everyone ?? false;
|
|
285
|
+
this.tts = data.tts ?? false;
|
|
286
|
+
this.embeds = data.embeds ?? [];
|
|
287
|
+
this.stickers = data.stickers ?? [];
|
|
288
|
+
this.reactions = data.reactions ?? [];
|
|
289
|
+
this.messageReference = data.message_reference ?? null;
|
|
290
|
+
this.messageSnapshots = data.message_snapshots ?? [];
|
|
291
|
+
this.call = data.call ?? null;
|
|
292
|
+
this.referencedMessage = data.referenced_message ? new _Message(client, data.referenced_message) : null;
|
|
293
|
+
this.webhookId = data.webhook_id ?? null;
|
|
294
|
+
this.mentions = (data.mentions ?? []).map((u) => client.getOrCreateUser(u));
|
|
295
|
+
this.mentionRoles = data.mention_roles ?? [];
|
|
296
|
+
this.nonce = data.nonce ?? null;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Send a message to this channel without replying. Use when you want a standalone message.
|
|
300
|
+
* @param options - Text content or object with content, embeds, and/or files
|
|
301
|
+
* @example
|
|
302
|
+
* await message.send('Pong!');
|
|
303
|
+
* await message.send({ embeds: [embed] }); // EmbedBuilder auto-converted
|
|
304
|
+
* await message.send({ content: 'File', files: [{ name: 'data.txt', data }] });
|
|
305
|
+
*/
|
|
306
|
+
async send(options) {
|
|
307
|
+
const payload = await _Message._createMessageBody(options);
|
|
308
|
+
return this._send(payload);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Send a message to a specific channel. Use for logging, forwarding, or sending to another channel in the guild.
|
|
312
|
+
* @param channelId - Snowflake of the target channel (e.g. log channel ID)
|
|
313
|
+
* @param options - Text content or object with content and/or embeds
|
|
314
|
+
* @example
|
|
315
|
+
* await message.sendTo(logChannelId, 'User ' + message.author.username + ' said: ' + message.content);
|
|
316
|
+
* await message.sendTo(announceChannelId, { embeds: [embed] });
|
|
317
|
+
*/
|
|
318
|
+
async sendTo(channelId, options) {
|
|
319
|
+
return this.client.channels.send(channelId, options);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Reply to this message (shows as a reply in the client).
|
|
323
|
+
* @param options - Text content or object with content, embeds, and/or reply options (ping, replyTo)
|
|
324
|
+
* @example
|
|
325
|
+
* await message.reply('Pong!');
|
|
326
|
+
* await message.reply({ embeds: [embed] });
|
|
327
|
+
* await message.reply('No ping!', { ping: false });
|
|
328
|
+
* await message.reply({ content: 'Reply to other', replyTo: otherMessage });
|
|
329
|
+
*/
|
|
330
|
+
async reply(options, replyOptions) {
|
|
331
|
+
const opts = typeof options === "string" ? { content: options } : options;
|
|
332
|
+
const mergedReply = replyOptions ?? (opts.ping !== void 0 || opts.replyTo !== void 0) ? { ping: opts.ping, replyTo: opts.replyTo } : void 0;
|
|
333
|
+
const refMessage = mergedReply?.replyTo ?? this;
|
|
334
|
+
const ref = refMessage instanceof _Message ? {
|
|
335
|
+
channel_id: refMessage.channelId,
|
|
336
|
+
message_id: refMessage.id,
|
|
337
|
+
guild_id: refMessage.guildId ?? void 0
|
|
338
|
+
} : {
|
|
339
|
+
channel_id: refMessage.channelId,
|
|
340
|
+
message_id: refMessage.messageId,
|
|
341
|
+
guild_id: void 0
|
|
342
|
+
};
|
|
343
|
+
const payload = await _Message._createMessageBody(opts, ref, mergedReply?.ping !== false);
|
|
344
|
+
return this._send(payload);
|
|
345
|
+
}
|
|
346
|
+
/** Exposed for testing purposes, use Message.reply() or send() for normal use */
|
|
347
|
+
static async _createMessageBody(content, referenced_message, ping) {
|
|
348
|
+
if (typeof content === "string") {
|
|
349
|
+
if (content.length === 0) {
|
|
350
|
+
throw new RangeError("Cannot send an empty message");
|
|
351
|
+
}
|
|
352
|
+
content = { content };
|
|
353
|
+
}
|
|
354
|
+
const base = buildSendBody(content);
|
|
355
|
+
const files = content.files?.length ? await resolveMessageFiles(content.files) : void 0;
|
|
356
|
+
const body = { ...base };
|
|
357
|
+
if (referenced_message) {
|
|
358
|
+
body.message_reference = referenced_message;
|
|
359
|
+
if (ping === false) {
|
|
360
|
+
body.flags = (body.flags ?? 0) | MessageFlags.SuppressNotifications;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return { files, body };
|
|
364
|
+
}
|
|
365
|
+
async _send(payload) {
|
|
366
|
+
const data = await this.client.rest.post(
|
|
367
|
+
Routes.channelMessages(this.channelId),
|
|
368
|
+
payload
|
|
369
|
+
);
|
|
370
|
+
this.client._addMessageToCache(this.channelId, data);
|
|
371
|
+
return new _Message(this.client, data);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Edit this message. Only the author (or admins) can edit.
|
|
375
|
+
* @param options - New content and/or embeds
|
|
376
|
+
*/
|
|
377
|
+
async edit(options) {
|
|
378
|
+
const body = {};
|
|
379
|
+
if (options.content !== void 0) body.content = options.content;
|
|
380
|
+
if (options.embeds?.length) {
|
|
381
|
+
body.embeds = options.embeds.map((e) => e instanceof EmbedBuilder2 ? e.toJSON() : e);
|
|
382
|
+
}
|
|
383
|
+
const data = await this.client.rest.patch(Routes.channelMessage(this.channelId, this.id), {
|
|
384
|
+
body
|
|
385
|
+
});
|
|
386
|
+
return new _Message(this.client, data);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Create a reaction collector for this message.
|
|
390
|
+
* Collects reactions matching the filter until time expires or max is reached.
|
|
391
|
+
* @param options - Filter, time (ms), and max count
|
|
392
|
+
* @example
|
|
393
|
+
* const collector = message.createReactionCollector({ filter: (r, u) => u.id === userId, time: 10000 });
|
|
394
|
+
* collector.on('collect', (reaction, user) => console.log(user.username, 'reacted with', reaction.emoji.name));
|
|
395
|
+
* collector.on('end', (collected, reason) => { ... });
|
|
396
|
+
*/
|
|
397
|
+
createReactionCollector(options) {
|
|
398
|
+
return new ReactionCollector(this.client, this.id, this.channelId, options);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Re-fetch this message from the API to get the latest content, embeds, reactions, etc.
|
|
402
|
+
* Use when you have a stale Message (e.g. from an old event or cache) and need fresh data.
|
|
403
|
+
* @returns The updated message
|
|
404
|
+
* @throws FluxerError with MESSAGE_NOT_FOUND if the message was deleted or does not exist
|
|
405
|
+
* @example
|
|
406
|
+
* const updated = await message.fetch();
|
|
407
|
+
* console.log('Latest content:', updated.content);
|
|
408
|
+
*/
|
|
409
|
+
async fetch() {
|
|
410
|
+
return this.client.channels.fetchMessage(this.channelId, this.id);
|
|
411
|
+
}
|
|
412
|
+
/** Delete this message. */
|
|
413
|
+
async delete() {
|
|
414
|
+
await this.client.rest.delete(Routes.channelMessage(this.channelId, this.id));
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Delete a specific attachment from this message.
|
|
418
|
+
* DELETE /channels/{id}/messages/{id}/attachments/{attachmentId}.
|
|
419
|
+
*/
|
|
420
|
+
async deleteAttachment(attachmentId) {
|
|
421
|
+
await this.client.rest.delete(
|
|
422
|
+
Routes.channelMessageAttachment(this.channelId, this.id, attachmentId),
|
|
423
|
+
{ auth: true }
|
|
424
|
+
);
|
|
425
|
+
this.attachments.delete(attachmentId);
|
|
426
|
+
}
|
|
427
|
+
/** Pin this message to the channel. Requires Manage Messages permission. */
|
|
428
|
+
async pin() {
|
|
429
|
+
await this.client.rest.put(Routes.channelPinMessage(this.channelId, this.id));
|
|
430
|
+
this.pinned = true;
|
|
431
|
+
}
|
|
432
|
+
/** Unpin this message from the channel. Requires Manage Messages permission. */
|
|
433
|
+
async unpin() {
|
|
434
|
+
await this.client.rest.delete(Routes.channelPinMessage(this.channelId, this.id));
|
|
435
|
+
this.pinned = false;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Format emoji for reaction API: unicode string or "name:id" for custom.
|
|
439
|
+
* For string resolution (e.g. :name:), use client.resolveEmoji; Message methods resolve automatically when guildId is available.
|
|
440
|
+
*/
|
|
441
|
+
static formatEmoji(emoji) {
|
|
442
|
+
if (typeof emoji === "string") return emoji;
|
|
443
|
+
return `${emoji.name}:${emoji.id}`;
|
|
444
|
+
}
|
|
445
|
+
resolveEmojiForReaction(emoji) {
|
|
446
|
+
return this.client.resolveEmoji(emoji, this.guildId);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Add a reaction to this message (as the bot).
|
|
450
|
+
* @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
|
|
451
|
+
*/
|
|
452
|
+
async react(emoji) {
|
|
453
|
+
const emojiStr = await this.resolveEmojiForReaction(emoji);
|
|
454
|
+
const route = `${Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/@me`;
|
|
455
|
+
await this.client.rest.put(route);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Remove the bot's reaction, or a specific user's reaction if userId is provided.
|
|
459
|
+
* @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
|
|
460
|
+
* @param userId - If provided, removes that user's reaction (requires moderator permissions)
|
|
461
|
+
*/
|
|
462
|
+
async removeReaction(emoji, userId) {
|
|
463
|
+
const emojiStr = await this.resolveEmojiForReaction(emoji);
|
|
464
|
+
const route = `${Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/${userId ?? "@me"}`;
|
|
465
|
+
await this.client.rest.delete(route);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Remove all reactions from this message.
|
|
469
|
+
* Requires moderator permissions.
|
|
470
|
+
*/
|
|
471
|
+
async removeAllReactions() {
|
|
472
|
+
await this.client.rest.delete(Routes.channelMessageReactions(this.channelId, this.id));
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Remove all reactions of a specific emoji from this message.
|
|
476
|
+
* @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`. Requires moderator permissions.
|
|
477
|
+
*/
|
|
478
|
+
async removeReactionEmoji(emoji) {
|
|
479
|
+
const emojiStr = await this.resolveEmojiForReaction(emoji);
|
|
480
|
+
await this.client.rest.delete(Routes.channelMessageReaction(this.channelId, this.id, emojiStr));
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Fetch users who reacted with the given emoji.
|
|
484
|
+
* @param emoji - Unicode emoji or custom `{ name, id }`
|
|
485
|
+
* @param options - limit (1–100), after (user ID for pagination)
|
|
486
|
+
* @returns Array of User objects
|
|
487
|
+
*/
|
|
488
|
+
async fetchReactionUsers(emoji, options) {
|
|
489
|
+
const emojiStr = await this.resolveEmojiForReaction(emoji);
|
|
490
|
+
const params = new URLSearchParams();
|
|
491
|
+
if (options?.limit != null) params.set("limit", String(options.limit));
|
|
492
|
+
if (options?.after) params.set("after", options.after);
|
|
493
|
+
const qs = params.toString();
|
|
494
|
+
const route = Routes.channelMessageReaction(this.channelId, this.id, emojiStr) + (qs ? `?${qs}` : "");
|
|
495
|
+
const data = await this.client.rest.get(route);
|
|
496
|
+
const list = Array.isArray(data) ? data : data?.users ?? [];
|
|
497
|
+
return list.map((u) => this.client.getOrCreateUser(u));
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// src/client/MessageManager.ts
|
|
502
|
+
var MessageManager = class {
|
|
503
|
+
constructor(client, channelId) {
|
|
504
|
+
this.client = client;
|
|
505
|
+
this.channelId = channelId;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Get a message from cache. Returns undefined if not cached or caching is disabled.
|
|
509
|
+
* Requires options.cache.messages > 0.
|
|
510
|
+
* @param messageId - Snowflake of the message
|
|
511
|
+
*/
|
|
512
|
+
get(messageId) {
|
|
513
|
+
const data = this.client._getMessageCache(this.channelId)?.get(messageId);
|
|
514
|
+
return data ? new Message(this.client, data) : void 0;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Fetch a message by ID from this channel.
|
|
518
|
+
* When message caching is enabled, the fetched message is added to the cache.
|
|
519
|
+
* @param messageId - Snowflake of the message
|
|
520
|
+
* @returns The message
|
|
521
|
+
* @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
|
|
522
|
+
*/
|
|
523
|
+
async fetch(messageId) {
|
|
524
|
+
try {
|
|
525
|
+
const data = await this.client.rest.get(
|
|
526
|
+
Routes2.channelMessage(this.channelId, messageId)
|
|
527
|
+
);
|
|
528
|
+
this.client._addMessageToCache(this.channelId, data);
|
|
529
|
+
return new Message(this.client, data);
|
|
530
|
+
} catch (err) {
|
|
531
|
+
if (err instanceof RateLimitError) throw err;
|
|
532
|
+
if (err instanceof FluxerAPIError && err.statusCode === 404) {
|
|
533
|
+
throw new FluxerError(`Message ${messageId} not found in channel ${this.channelId}`, {
|
|
534
|
+
code: ErrorCodes.MessageNotFound,
|
|
535
|
+
cause: err
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
// src/util/MessageCollector.ts
|
|
544
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
545
|
+
import { Collection as Collection3 } from "@erinjs/collection";
|
|
546
|
+
var MessageCollector = class extends EventEmitter2 {
|
|
547
|
+
client;
|
|
548
|
+
channelId;
|
|
549
|
+
options;
|
|
550
|
+
collected = new Collection3();
|
|
551
|
+
_timeout = null;
|
|
552
|
+
_ended = false;
|
|
553
|
+
_listener;
|
|
554
|
+
constructor(client, channelId, options = {}) {
|
|
555
|
+
super();
|
|
556
|
+
this.client = client;
|
|
557
|
+
this.channelId = channelId;
|
|
558
|
+
this.options = {
|
|
559
|
+
filter: options.filter ?? (() => true),
|
|
560
|
+
time: options.time ?? 0,
|
|
561
|
+
max: options.max ?? 0
|
|
562
|
+
};
|
|
563
|
+
this._listener = (message) => {
|
|
564
|
+
if (this._ended || message.channelId !== this.channelId) return;
|
|
565
|
+
if (!this.options.filter(message)) return;
|
|
566
|
+
this.collected.set(message.id, message);
|
|
567
|
+
this.emit("collect", message);
|
|
568
|
+
if (this.options.max > 0 && this.collected.size >= this.options.max) {
|
|
569
|
+
this.stop("limit");
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
this.client.on(Events.MessageCreate, this._listener);
|
|
573
|
+
if (this.options.time > 0) {
|
|
574
|
+
this._timeout = setTimeout(() => this.stop("time"), this.options.time);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
stop(reason = "user") {
|
|
578
|
+
if (this._ended) return;
|
|
579
|
+
this._ended = true;
|
|
580
|
+
this.client.off(Events.MessageCreate, this._listener);
|
|
581
|
+
if (this._timeout) {
|
|
582
|
+
clearTimeout(this._timeout);
|
|
583
|
+
this._timeout = null;
|
|
584
|
+
}
|
|
585
|
+
this.emit("end", this.collected, reason);
|
|
586
|
+
}
|
|
587
|
+
on(event, listener) {
|
|
588
|
+
return super.on(event, listener);
|
|
589
|
+
}
|
|
590
|
+
emit(event, ...args) {
|
|
591
|
+
return super.emit(event, ...args);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/structures/Channel.ts
|
|
596
|
+
import { ChannelType, Routes as Routes5 } from "@erinjs/types";
|
|
597
|
+
import { PermissionFlags } from "@erinjs/util";
|
|
598
|
+
import { emitDeprecationWarning } from "@erinjs/util";
|
|
599
|
+
|
|
600
|
+
// src/structures/Webhook.ts
|
|
601
|
+
import { Routes as Routes3 } from "@erinjs/types";
|
|
602
|
+
|
|
603
|
+
// src/util/Constants.ts
|
|
604
|
+
var CDN_URL = "https://fluxerusercontent.com";
|
|
605
|
+
var STATIC_CDN_URL = "https://fluxerstatic.com";
|
|
606
|
+
|
|
607
|
+
// src/util/cdn.ts
|
|
608
|
+
function getExtension(hash, options) {
|
|
609
|
+
const ext = options?.extension ?? "png";
|
|
610
|
+
if (hash?.startsWith("a_")) return "gif";
|
|
611
|
+
return ext;
|
|
612
|
+
}
|
|
613
|
+
function appendSize(options) {
|
|
614
|
+
return options?.size ? `?size=${options.size}` : "";
|
|
615
|
+
}
|
|
616
|
+
function cdnAvatarURL(userId, avatarHash, options) {
|
|
617
|
+
if (!avatarHash) return null;
|
|
618
|
+
const ext = getExtension(avatarHash, options);
|
|
619
|
+
const size = appendSize(options);
|
|
620
|
+
return `${CDN_URL}/avatars/${userId}/${avatarHash}.${ext}${size}`;
|
|
621
|
+
}
|
|
622
|
+
function cdnDisplayAvatarURL(userId, avatarHash, options) {
|
|
623
|
+
return cdnAvatarURL(userId, avatarHash, options) ?? cdnDefaultAvatarURL(userId);
|
|
624
|
+
}
|
|
625
|
+
function cdnBannerURL(resourceId, bannerHash, options) {
|
|
626
|
+
if (!bannerHash) return null;
|
|
627
|
+
const ext = getExtension(bannerHash, options);
|
|
628
|
+
const size = appendSize(options);
|
|
629
|
+
return `${CDN_URL}/banners/${resourceId}/${bannerHash}.${ext}${size}`;
|
|
630
|
+
}
|
|
631
|
+
function cdnMemberAvatarURL(guildId, userId, avatarHash, options) {
|
|
632
|
+
if (!avatarHash) return null;
|
|
633
|
+
const ext = getExtension(avatarHash, options);
|
|
634
|
+
const size = appendSize(options);
|
|
635
|
+
return `${CDN_URL}/guilds/${guildId}/users/${userId}/avatars/${avatarHash}.${ext}${size}`;
|
|
636
|
+
}
|
|
637
|
+
function cdnMemberBannerURL(guildId, userId, bannerHash, options) {
|
|
638
|
+
if (!bannerHash) return null;
|
|
639
|
+
const ext = getExtension(bannerHash, options);
|
|
640
|
+
const size = appendSize(options);
|
|
641
|
+
return `${CDN_URL}/guilds/${guildId}/users/${userId}/banners/${bannerHash}.${ext}${size}`;
|
|
642
|
+
}
|
|
643
|
+
function cdnDefaultAvatarURL(userIdOrIndex) {
|
|
644
|
+
const index = typeof userIdOrIndex === "string" ? Number(BigInt(userIdOrIndex) % 6n) : Math.abs(Math.floor(userIdOrIndex) % 6);
|
|
645
|
+
return `${STATIC_CDN_URL}/avatars/${index}.png`;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/structures/Webhook.ts
|
|
649
|
+
var Webhook = class _Webhook extends Base {
|
|
650
|
+
client;
|
|
651
|
+
id;
|
|
652
|
+
guildId;
|
|
653
|
+
channelId;
|
|
654
|
+
name;
|
|
655
|
+
avatar;
|
|
656
|
+
/** Present only when webhook was created via createWebhook(); not returned when fetching. */
|
|
657
|
+
token;
|
|
658
|
+
/** User who created the webhook. */
|
|
659
|
+
user;
|
|
660
|
+
/** @param data - API webhook from POST /channels/{id}/webhooks (has token) or GET /webhooks/{id} (no token) */
|
|
661
|
+
constructor(client, data) {
|
|
662
|
+
super();
|
|
663
|
+
this.client = client;
|
|
664
|
+
this.id = data.id;
|
|
665
|
+
this.guildId = data.guild_id;
|
|
666
|
+
this.channelId = data.channel_id;
|
|
667
|
+
this.name = data.name ?? "Unknown";
|
|
668
|
+
this.avatar = data.avatar ?? null;
|
|
669
|
+
this.token = data.token ?? null;
|
|
670
|
+
this.user = client.getOrCreateUser(data.user);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Get the URL for this webhook's avatar.
|
|
674
|
+
* Returns null if the webhook has no custom avatar.
|
|
675
|
+
*/
|
|
676
|
+
avatarURL(options) {
|
|
677
|
+
return cdnAvatarURL(this.id, this.avatar, options);
|
|
678
|
+
}
|
|
679
|
+
/** Delete this webhook. Requires bot token with Manage Webhooks permission. */
|
|
680
|
+
async delete() {
|
|
681
|
+
await this.client.rest.delete(Routes3.webhook(this.id), { auth: true });
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Edit this webhook. With token: name and avatar only. Without token (bot auth): name, avatar, and channel_id.
|
|
685
|
+
* @param options - Fields to update (name, avatar, channel_id when using bot auth)
|
|
686
|
+
* @returns This webhook instance with updated fields
|
|
687
|
+
*/
|
|
688
|
+
async edit(options) {
|
|
689
|
+
const body = {};
|
|
690
|
+
if (options.name !== void 0) body.name = options.name;
|
|
691
|
+
if (options.avatar !== void 0) body.avatar = options.avatar;
|
|
692
|
+
if ("channel_id" in options && options.channel_id !== void 0 && !this.token) {
|
|
693
|
+
body.channel_id = options.channel_id;
|
|
694
|
+
}
|
|
695
|
+
if (this.token) {
|
|
696
|
+
const data2 = await this.client.rest.patch(Routes3.webhookExecute(this.id, this.token), {
|
|
697
|
+
body,
|
|
698
|
+
auth: false
|
|
699
|
+
});
|
|
700
|
+
const w2 = data2;
|
|
701
|
+
this.name = w2.name ?? this.name;
|
|
702
|
+
this.avatar = w2.avatar ?? null;
|
|
703
|
+
return this;
|
|
704
|
+
}
|
|
705
|
+
const data = await this.client.rest.patch(Routes3.webhook(this.id), {
|
|
706
|
+
body,
|
|
707
|
+
auth: true
|
|
708
|
+
});
|
|
709
|
+
const w = data;
|
|
710
|
+
this.name = w.name ?? this.name;
|
|
711
|
+
this.avatar = w.avatar ?? null;
|
|
712
|
+
this.channelId = w.channel_id ?? this.channelId;
|
|
713
|
+
return this;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Send a message via this webhook. Requires the webhook token (only present when created, not when fetched).
|
|
717
|
+
* @param options - Text content or object with content, embeds, username, avatar_url, tts, files, attachments
|
|
718
|
+
* @param wait - If true, waits for the API and returns the created Message; otherwise returns void (204)
|
|
719
|
+
* @throws Error if token is not available
|
|
720
|
+
* @example
|
|
721
|
+
* await webhook.send('Hello!');
|
|
722
|
+
* await webhook.send({ embeds: [embed] });
|
|
723
|
+
* await webhook.send({ content: 'File attached', files: [{ name: 'data.txt', data: buffer }] });
|
|
724
|
+
* const msg = await webhook.send({ content: 'Hi' }, true);
|
|
725
|
+
*/
|
|
726
|
+
async send(options, wait) {
|
|
727
|
+
if (!this.token) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
"Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send."
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
const opts = typeof options === "string" ? { content: options } : options;
|
|
733
|
+
const body = buildSendBody(opts);
|
|
734
|
+
if (opts.username !== void 0) body.username = opts.username;
|
|
735
|
+
if (opts.avatar_url !== void 0) body.avatar_url = opts.avatar_url;
|
|
736
|
+
if (opts.tts !== void 0) body.tts = opts.tts;
|
|
737
|
+
const route = Routes3.webhookExecute(this.id, this.token) + (wait ? "?wait=true" : "");
|
|
738
|
+
const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
|
|
739
|
+
const postOptions = files?.length ? { body, files, auth: false } : { body, auth: false };
|
|
740
|
+
const data = await this.client.rest.post(route, postOptions);
|
|
741
|
+
if (wait && data) {
|
|
742
|
+
return new Message(this.client, data);
|
|
743
|
+
}
|
|
744
|
+
return void 0;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Edit a message previously sent by this webhook.
|
|
748
|
+
* Requires the webhook token.
|
|
749
|
+
* @param messageId - The ID of the message to edit
|
|
750
|
+
* @param options - Fields to update (content, embeds, attachments)
|
|
751
|
+
* @returns The updated Message
|
|
752
|
+
* @throws Error if token is not available
|
|
753
|
+
*/
|
|
754
|
+
async editMessage(messageId, options) {
|
|
755
|
+
if (!this.token) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"Webhook token is required to edit messages. The token is only returned when creating a webhook; fetched webhooks cannot edit messages."
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
const data = await this.client.rest.patch(
|
|
761
|
+
Routes3.webhookMessage(this.id, this.token, messageId),
|
|
762
|
+
{ body: options, auth: false }
|
|
763
|
+
);
|
|
764
|
+
return new Message(this.client, data);
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Fetch a message sent by this webhook.
|
|
768
|
+
* Requires the webhook token.
|
|
769
|
+
* @param messageId - The ID of the message to fetch
|
|
770
|
+
* @returns The message
|
|
771
|
+
* @throws Error if token is not available
|
|
772
|
+
*/
|
|
773
|
+
async fetchMessage(messageId) {
|
|
774
|
+
if (!this.token) {
|
|
775
|
+
throw new Error(
|
|
776
|
+
"Webhook token is required to fetch messages. The token is only returned when creating a webhook; fetched webhooks cannot fetch messages."
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
const data = await this.client.rest.get(
|
|
780
|
+
Routes3.webhookMessage(this.id, this.token, messageId),
|
|
781
|
+
{ auth: false }
|
|
782
|
+
);
|
|
783
|
+
return new Message(this.client, data);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Delete a message sent by this webhook.
|
|
787
|
+
* Requires the webhook token.
|
|
788
|
+
* @param messageId - The ID of the message to delete
|
|
789
|
+
* @throws Error if token is not available
|
|
790
|
+
*/
|
|
791
|
+
async deleteMessage(messageId) {
|
|
792
|
+
if (!this.token) {
|
|
793
|
+
throw new Error(
|
|
794
|
+
"Webhook token is required to delete messages. The token is only returned when creating a webhook; fetched webhooks cannot delete messages."
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
await this.client.rest.delete(Routes3.webhookMessage(this.id, this.token, messageId), {
|
|
798
|
+
auth: false
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Fetch a webhook by ID using bot auth.
|
|
803
|
+
* @param client - The client instance
|
|
804
|
+
* @param webhookId - The webhook ID
|
|
805
|
+
* @returns Webhook without token (cannot send)
|
|
806
|
+
*/
|
|
807
|
+
static async fetch(client, webhookId) {
|
|
808
|
+
const data = await client.rest.get(Routes3.webhook(webhookId));
|
|
809
|
+
return new _Webhook(client, data);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Create a Webhook instance from an ID and token (e.g. from a stored webhook URL).
|
|
813
|
+
* @param client - The client instance
|
|
814
|
+
* @param webhookId - The webhook ID
|
|
815
|
+
* @param token - The webhook token (from createWebhook or stored)
|
|
816
|
+
* @param options - Optional channelId, guildId, name for display
|
|
817
|
+
*/
|
|
818
|
+
static fromToken(client, webhookId, token, options) {
|
|
819
|
+
return new _Webhook(client, {
|
|
820
|
+
id: webhookId,
|
|
821
|
+
guild_id: options?.guildId ?? "",
|
|
822
|
+
channel_id: options?.channelId ?? "",
|
|
823
|
+
name: options?.name ?? "Webhook",
|
|
824
|
+
avatar: null,
|
|
825
|
+
token,
|
|
826
|
+
user: { id: "", username: "webhook", discriminator: "0" }
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/structures/Invite.ts
|
|
832
|
+
import { Routes as Routes4 } from "@erinjs/types";
|
|
833
|
+
var Invite = class _Invite extends Base {
|
|
834
|
+
client;
|
|
835
|
+
code;
|
|
836
|
+
type;
|
|
837
|
+
guild;
|
|
838
|
+
channel;
|
|
839
|
+
inviter;
|
|
840
|
+
memberCount;
|
|
841
|
+
presenceCount;
|
|
842
|
+
expiresAt;
|
|
843
|
+
temporary;
|
|
844
|
+
createdAt;
|
|
845
|
+
uses;
|
|
846
|
+
maxUses;
|
|
847
|
+
maxAge;
|
|
848
|
+
/**
|
|
849
|
+
* Normalize an invite input into a raw invite code.
|
|
850
|
+
* Accepts either an invite code (e.g. "abc123") or a URL (e.g. "https://fluxer.gg/abc123").
|
|
851
|
+
*/
|
|
852
|
+
static normalizeCode(codeOrUrl) {
|
|
853
|
+
const input = codeOrUrl.trim();
|
|
854
|
+
if (!input) {
|
|
855
|
+
throw new RangeError("Invite code cannot be empty");
|
|
856
|
+
}
|
|
857
|
+
const parseAsUrl = (value) => {
|
|
858
|
+
try {
|
|
859
|
+
const url = new URL(value);
|
|
860
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
861
|
+
if (segments.length === 0) {
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
const inviteSegmentIdx = segments.findIndex((segment) => {
|
|
865
|
+
const lower = segment.toLowerCase();
|
|
866
|
+
return lower === "invite" || lower === "invites";
|
|
867
|
+
});
|
|
868
|
+
const code2 = inviteSegmentIdx >= 0 && segments[inviteSegmentIdx + 1] ? segments[inviteSegmentIdx + 1] : segments[segments.length - 1];
|
|
869
|
+
return decodeURIComponent(code2).trim();
|
|
870
|
+
} catch {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
const directCode = decodeURIComponent(input).trim();
|
|
875
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(input);
|
|
876
|
+
const fromAbsoluteUrl = parseAsUrl(input);
|
|
877
|
+
const fromHostLike = hasScheme ? null : parseAsUrl(`https://${input}`);
|
|
878
|
+
const code = fromAbsoluteUrl ?? fromHostLike ?? directCode;
|
|
879
|
+
if (!code || /[\s/?#]/.test(code)) {
|
|
880
|
+
throw new RangeError("Invalid invite code or URL");
|
|
881
|
+
}
|
|
882
|
+
return code;
|
|
883
|
+
}
|
|
884
|
+
/** @param data - API invite from GET /invites/{code}, channel/guild invite list, or gateway INVITE_CREATE */
|
|
885
|
+
constructor(client, data) {
|
|
886
|
+
super();
|
|
887
|
+
this.client = client;
|
|
888
|
+
this.code = data.code;
|
|
889
|
+
this.type = data.type;
|
|
890
|
+
this.guild = data.guild;
|
|
891
|
+
this.channel = data.channel;
|
|
892
|
+
this.inviter = data.inviter ? client.getOrCreateUser(data.inviter) : null;
|
|
893
|
+
this.memberCount = data.member_count ?? null;
|
|
894
|
+
this.presenceCount = data.presence_count ?? null;
|
|
895
|
+
this.expiresAt = data.expires_at ?? null;
|
|
896
|
+
this.temporary = data.temporary ?? null;
|
|
897
|
+
this.createdAt = data.created_at ?? null;
|
|
898
|
+
this.uses = data.uses ?? null;
|
|
899
|
+
this.maxUses = data.max_uses ?? null;
|
|
900
|
+
this.maxAge = data.max_age ?? null;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Fetch invite information by code or URL.
|
|
904
|
+
* Does not consume or accept the invite.
|
|
905
|
+
*/
|
|
906
|
+
static async fetch(client, codeOrUrl) {
|
|
907
|
+
const code = _Invite.normalizeCode(codeOrUrl);
|
|
908
|
+
const data = await client.rest.get(Routes4.invite(code));
|
|
909
|
+
return new _Invite(client, data);
|
|
910
|
+
}
|
|
911
|
+
/** Full invite URL (https://fluxer.gg/{code} or instance-specific). */
|
|
912
|
+
get url() {
|
|
913
|
+
return `https://fluxer.gg/${this.code}`;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Resolve the guild from cache if available.
|
|
917
|
+
* @returns The guild, or null if not cached
|
|
918
|
+
*/
|
|
919
|
+
getGuild() {
|
|
920
|
+
return this.guild?.id ? this.client.guilds.get(this.guild.id) ?? null : null;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Delete this invite.
|
|
924
|
+
* Requires Manage Guild or Create Instant Invite permission.
|
|
925
|
+
*/
|
|
926
|
+
async delete() {
|
|
927
|
+
await this.client.rest.delete(Routes4.invite(this.code), { auth: true });
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
// src/structures/Channel.ts
|
|
932
|
+
var Channel = class _Channel extends Base {
|
|
933
|
+
/** Whether this channel has a send method (TextChannel, DMChannel). */
|
|
934
|
+
isTextBased() {
|
|
935
|
+
return "send" in this;
|
|
936
|
+
}
|
|
937
|
+
/** Whether this channel is a DM or Group DM. */
|
|
938
|
+
isDM() {
|
|
939
|
+
return this.type === ChannelType.DM || this.type === ChannelType.GroupDM;
|
|
940
|
+
}
|
|
941
|
+
/** Whether this channel is voice-based (VoiceChannel). */
|
|
942
|
+
isVoice() {
|
|
943
|
+
return "bitrate" in this;
|
|
944
|
+
}
|
|
945
|
+
isLink() {
|
|
946
|
+
return "url" in this;
|
|
947
|
+
}
|
|
948
|
+
/** Create a DM channel from API data (type DM or GroupDM). */
|
|
949
|
+
static createDM(client, data) {
|
|
950
|
+
return new DMChannel(client, data);
|
|
951
|
+
}
|
|
952
|
+
client;
|
|
953
|
+
id;
|
|
954
|
+
type;
|
|
955
|
+
/** Channel name. Guild channels and Group DMs have names; 1:1 DMs are typically null. */
|
|
956
|
+
name;
|
|
957
|
+
/** Channel icon hash (Group DMs). Null if none. */
|
|
958
|
+
icon;
|
|
959
|
+
/** ISO timestamp when the last message was pinned. Null if never pinned. */
|
|
960
|
+
lastPinTimestamp;
|
|
961
|
+
/** @param data - API channel from GET /channels/{id} or GET /guilds/{id}/channels */
|
|
962
|
+
constructor(client, data) {
|
|
963
|
+
super();
|
|
964
|
+
this.client = client;
|
|
965
|
+
this.id = data.id;
|
|
966
|
+
this.type = data.type;
|
|
967
|
+
this.name = data.name ?? null;
|
|
968
|
+
this.icon = data.icon ?? null;
|
|
969
|
+
this.lastPinTimestamp = data.last_pin_timestamp ?? null;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Create the appropriate channel subclass from API data.
|
|
973
|
+
* @param client - The client instance
|
|
974
|
+
* @param data - Channel data from the API
|
|
975
|
+
*/
|
|
976
|
+
static from(client, data) {
|
|
977
|
+
const type = data.type ?? 0;
|
|
978
|
+
if (type === ChannelType.GuildText) return new TextChannel(client, data);
|
|
979
|
+
if (type === ChannelType.GuildCategory) return new CategoryChannel(client, data);
|
|
980
|
+
if (type === ChannelType.GuildVoice) return new VoiceChannel(client, data);
|
|
981
|
+
if (type === ChannelType.GuildLink || type === ChannelType.GuildLinkExtended)
|
|
982
|
+
return new LinkChannel(client, data);
|
|
983
|
+
return new GuildChannel(client, data);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Create a channel from API data, including DM and GroupDM.
|
|
987
|
+
* Used by ChannelManager.fetch() for GET /channels/{id}.
|
|
988
|
+
*/
|
|
989
|
+
static fromOrCreate(client, data) {
|
|
990
|
+
const type = data.type ?? 0;
|
|
991
|
+
if (type === ChannelType.DM || type === ChannelType.GroupDM)
|
|
992
|
+
return _Channel.createDM(client, data);
|
|
993
|
+
return _Channel.from(client, data);
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Bulk delete messages. Requires Manage Messages permission.
|
|
997
|
+
* @param messageIds - Array of message IDs to delete (2–100)
|
|
998
|
+
*/
|
|
999
|
+
async bulkDeleteMessages(messageIds) {
|
|
1000
|
+
await this.client.rest.post(Routes5.channelBulkDelete(this.id), {
|
|
1001
|
+
body: { message_ids: messageIds },
|
|
1002
|
+
auth: true
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Send a typing indicator to the channel. Lasts ~10 seconds.
|
|
1007
|
+
*/
|
|
1008
|
+
async sendTyping() {
|
|
1009
|
+
await this.client.rest.post(Routes5.channelTyping(this.id), { auth: true });
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Whether the bot can send messages in this channel.
|
|
1013
|
+
* For DMs: always true (when the channel exists).
|
|
1014
|
+
* For guild channels: checks ViewChannel and SendMessages permissions via guild.members.me.
|
|
1015
|
+
*/
|
|
1016
|
+
canSendMessage() {
|
|
1017
|
+
if (this.isDM()) return true;
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
var GuildChannel = class extends Channel {
|
|
1022
|
+
guildId;
|
|
1023
|
+
name;
|
|
1024
|
+
position;
|
|
1025
|
+
parentId;
|
|
1026
|
+
/** Permission overwrites for roles and members. */
|
|
1027
|
+
permissionOverwrites;
|
|
1028
|
+
constructor(client, data) {
|
|
1029
|
+
super(client, data);
|
|
1030
|
+
this.guildId = data.guild_id ?? "";
|
|
1031
|
+
this.name = data.name ?? null;
|
|
1032
|
+
this.position = data.position;
|
|
1033
|
+
this.parentId = data.parent_id ?? null;
|
|
1034
|
+
this.permissionOverwrites = data.permission_overwrites ?? [];
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Create a webhook in this channel.
|
|
1038
|
+
* @param options - Webhook name and optional avatar URL
|
|
1039
|
+
* @returns The webhook with token (required for send()). Requires Manage Webhooks permission.
|
|
1040
|
+
*/
|
|
1041
|
+
async createWebhook(options) {
|
|
1042
|
+
const data = await this.client.rest.post(Routes5.channelWebhooks(this.id), {
|
|
1043
|
+
body: options,
|
|
1044
|
+
auth: true
|
|
1045
|
+
});
|
|
1046
|
+
return new Webhook(this.client, data);
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Fetch all webhooks in this channel.
|
|
1050
|
+
* @returns Webhooks (includes token when listing from channel; can send via send())
|
|
1051
|
+
*/
|
|
1052
|
+
async fetchWebhooks() {
|
|
1053
|
+
const data = await this.client.rest.get(Routes5.channelWebhooks(this.id));
|
|
1054
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
1055
|
+
return list.map((w) => new Webhook(this.client, w));
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Create an invite for this channel.
|
|
1059
|
+
* @param options - max_uses (0–100), max_age (0–604800 seconds), unique, temporary
|
|
1060
|
+
* Requires Create Instant Invite permission.
|
|
1061
|
+
*/
|
|
1062
|
+
async createInvite(options) {
|
|
1063
|
+
const body = {};
|
|
1064
|
+
if (options?.max_uses != null) body.max_uses = options.max_uses;
|
|
1065
|
+
if (options?.max_age != null) body.max_age = options.max_age;
|
|
1066
|
+
if (options?.unique != null) body.unique = options.unique;
|
|
1067
|
+
if (options?.temporary != null) body.temporary = options.temporary;
|
|
1068
|
+
const data = await this.client.rest.post(Routes5.channelInvites(this.id), {
|
|
1069
|
+
body: Object.keys(body).length ? body : void 0,
|
|
1070
|
+
auth: true
|
|
1071
|
+
});
|
|
1072
|
+
return new Invite(this.client, data);
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Fetch invites for this channel.
|
|
1076
|
+
* Requires Manage Channel permission.
|
|
1077
|
+
*/
|
|
1078
|
+
async fetchInvites() {
|
|
1079
|
+
const data = await this.client.rest.get(Routes5.channelInvites(this.id));
|
|
1080
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
1081
|
+
return list.map((i) => new Invite(this.client, i));
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Set or update a permission overwrite. PUT /channels/{id}/permissions/{overwriteId}.
|
|
1085
|
+
* @param overwriteId - Role or member ID
|
|
1086
|
+
* @param options - type (0=role, 1=member), allow, deny (permission bitfields)
|
|
1087
|
+
*/
|
|
1088
|
+
async editPermission(overwriteId, options) {
|
|
1089
|
+
await this.client.rest.put(Routes5.channelPermission(this.id, overwriteId), {
|
|
1090
|
+
body: options,
|
|
1091
|
+
auth: true
|
|
1092
|
+
});
|
|
1093
|
+
const idx = this.permissionOverwrites.findIndex((o) => o.id === overwriteId);
|
|
1094
|
+
const entry = {
|
|
1095
|
+
id: overwriteId,
|
|
1096
|
+
type: options.type,
|
|
1097
|
+
allow: options.allow ?? "0",
|
|
1098
|
+
deny: options.deny ?? "0"
|
|
1099
|
+
};
|
|
1100
|
+
if (idx >= 0) this.permissionOverwrites[idx] = entry;
|
|
1101
|
+
else this.permissionOverwrites.push(entry);
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Whether the bot can send messages in this channel.
|
|
1105
|
+
* Checks ViewChannel and SendMessages via guild.members.me permissions.
|
|
1106
|
+
* Returns false if guild or bot member not cached.
|
|
1107
|
+
*/
|
|
1108
|
+
canSendMessage() {
|
|
1109
|
+
const guild = this.client.guilds.get(this.guildId);
|
|
1110
|
+
if (!guild) return false;
|
|
1111
|
+
const me = guild.members.me;
|
|
1112
|
+
if (!me) return false;
|
|
1113
|
+
const perms = me.permissionsIn(this);
|
|
1114
|
+
return perms.has(PermissionFlags.ViewChannel) && perms.has(PermissionFlags.SendMessages);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Send a message to this guild channel.
|
|
1118
|
+
* Works for text and announcement channels. Voice/category/link channels will fail at the API.
|
|
1119
|
+
*/
|
|
1120
|
+
async send(options) {
|
|
1121
|
+
const opts = typeof options === "string" ? { content: options } : options;
|
|
1122
|
+
const body = buildSendBody(options);
|
|
1123
|
+
const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
|
|
1124
|
+
const postOptions = files?.length ? { body, files } : { body };
|
|
1125
|
+
const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
|
|
1126
|
+
this.client._addMessageToCache(this.id, data);
|
|
1127
|
+
return new Message(this.client, data);
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Remove a permission overwrite. DELETE /channels/{id}/permissions/{overwriteId}.
|
|
1131
|
+
*/
|
|
1132
|
+
async deletePermission(overwriteId) {
|
|
1133
|
+
await this.client.rest.delete(Routes5.channelPermission(this.id, overwriteId), { auth: true });
|
|
1134
|
+
const idx = this.permissionOverwrites.findIndex((o) => o.id === overwriteId);
|
|
1135
|
+
if (idx >= 0) this.permissionOverwrites.splice(idx, 1);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Edit this channel. PATCH /channels/{id}.
|
|
1139
|
+
* Requires Manage Channel permission.
|
|
1140
|
+
*/
|
|
1141
|
+
async edit(options) {
|
|
1142
|
+
const data = await this.client.rest.patch(Routes5.channel(this.id), {
|
|
1143
|
+
body: options,
|
|
1144
|
+
auth: true
|
|
1145
|
+
});
|
|
1146
|
+
this.name = data.name ?? this.name;
|
|
1147
|
+
this.parentId = data.parent_id ?? this.parentId;
|
|
1148
|
+
this.permissionOverwrites = data.permission_overwrites ?? this.permissionOverwrites;
|
|
1149
|
+
const self = this;
|
|
1150
|
+
if ("topic" in self && "topic" in data) self.topic = data.topic ?? null;
|
|
1151
|
+
if ("nsfw" in self && "nsfw" in data) self.nsfw = data.nsfw ?? false;
|
|
1152
|
+
if ("rate_limit_per_user" in data) self.rateLimitPerUser = data.rate_limit_per_user ?? 0;
|
|
1153
|
+
if ("bitrate" in self && "bitrate" in data) self.bitrate = data.bitrate ?? null;
|
|
1154
|
+
if ("user_limit" in data) self.userLimit = data.user_limit ?? null;
|
|
1155
|
+
if ("rtc_region" in data) self.rtcRegion = data.rtc_region ?? null;
|
|
1156
|
+
return this;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Delete this channel. Requires Manage Channel permission.
|
|
1160
|
+
* @param options - silent: if true, does not send a system message (default false)
|
|
1161
|
+
*/
|
|
1162
|
+
async delete(options) {
|
|
1163
|
+
const url = Routes5.channel(this.id) + (options?.silent ? "?silent=true" : "");
|
|
1164
|
+
await this.client.rest.delete(url, { auth: true });
|
|
1165
|
+
this.client.channels.delete(this.id);
|
|
1166
|
+
const guild = this.client.guilds.get(this.guildId);
|
|
1167
|
+
if (guild) guild.channels.delete(this.id);
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
var TextChannel = class extends GuildChannel {
|
|
1171
|
+
topic;
|
|
1172
|
+
nsfw;
|
|
1173
|
+
rateLimitPerUser;
|
|
1174
|
+
lastMessageId;
|
|
1175
|
+
constructor(client, data) {
|
|
1176
|
+
super(client, data);
|
|
1177
|
+
this.topic = data.topic ?? null;
|
|
1178
|
+
this.nsfw = data.nsfw ?? false;
|
|
1179
|
+
this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
|
|
1180
|
+
this.lastMessageId = data.last_message_id ?? null;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Send a message to this channel.
|
|
1184
|
+
* @param options - Text content or object with content, embeds, and/or files
|
|
1185
|
+
*/
|
|
1186
|
+
async send(options) {
|
|
1187
|
+
const opts = typeof options === "string" ? { content: options } : options;
|
|
1188
|
+
const body = buildSendBody(options);
|
|
1189
|
+
const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
|
|
1190
|
+
const postOptions = files?.length ? { body, files } : { body };
|
|
1191
|
+
const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
|
|
1192
|
+
this.client._addMessageToCache(this.id, data);
|
|
1193
|
+
return new Message(this.client, data);
|
|
1194
|
+
}
|
|
1195
|
+
/** Message manager for this channel. Use channel.messages.fetch(messageId). */
|
|
1196
|
+
get messages() {
|
|
1197
|
+
return new MessageManager(this.client, this.id);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Create a message collector for this channel.
|
|
1201
|
+
* Collects messages matching the filter until time expires or max is reached.
|
|
1202
|
+
* @param options - Filter, time (ms), and max count
|
|
1203
|
+
* @example
|
|
1204
|
+
* const collector = channel.createMessageCollector({ filter: m => m.author.id === userId, time: 10000 });
|
|
1205
|
+
* collector.on('collect', m => console.log(m.content));
|
|
1206
|
+
* collector.on('end', (collected, reason) => { ... });
|
|
1207
|
+
*/
|
|
1208
|
+
createMessageCollector(options) {
|
|
1209
|
+
return new MessageCollector(this.client, this.id, options);
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Fetch pinned messages in this channel.
|
|
1213
|
+
* @returns Pinned messages
|
|
1214
|
+
*/
|
|
1215
|
+
async fetchPinnedMessages() {
|
|
1216
|
+
const data = await this.client.rest.get(Routes5.channelPins(this.id));
|
|
1217
|
+
const list = Array.isArray(data) ? data : data?.items ?? [];
|
|
1218
|
+
return list.map((item) => {
|
|
1219
|
+
const msg = typeof item === "object" && item && "message" in item ? item.message : item;
|
|
1220
|
+
return new Message(this.client, msg);
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Fetch a message by ID from this channel.
|
|
1225
|
+
* @param messageId - Snowflake of the message
|
|
1226
|
+
* @returns The message, or null if not found
|
|
1227
|
+
* @deprecated Use channel.messages.fetch(messageId) instead.
|
|
1228
|
+
*/
|
|
1229
|
+
async fetchMessage(messageId) {
|
|
1230
|
+
emitDeprecationWarning(
|
|
1231
|
+
"Channel.fetchMessage()",
|
|
1232
|
+
"Use channel.messages.fetch(messageId) instead."
|
|
1233
|
+
);
|
|
1234
|
+
return this.client.channels.fetchMessage(this.id, messageId);
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
var CategoryChannel = class extends GuildChannel {
|
|
1238
|
+
};
|
|
1239
|
+
var VoiceChannel = class extends GuildChannel {
|
|
1240
|
+
bitrate;
|
|
1241
|
+
userLimit;
|
|
1242
|
+
rtcRegion;
|
|
1243
|
+
constructor(client, data) {
|
|
1244
|
+
super(client, data);
|
|
1245
|
+
this.bitrate = data.bitrate ?? null;
|
|
1246
|
+
this.userLimit = data.user_limit ?? null;
|
|
1247
|
+
this.rtcRegion = data.rtc_region ?? null;
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
var LinkChannel = class extends GuildChannel {
|
|
1251
|
+
url;
|
|
1252
|
+
constructor(client, data) {
|
|
1253
|
+
super(client, data);
|
|
1254
|
+
this.url = data.url ?? null;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
var DMChannel = class extends Channel {
|
|
1258
|
+
lastMessageId;
|
|
1259
|
+
/** Group DM creator ID. Null for 1:1 DMs. */
|
|
1260
|
+
ownerId;
|
|
1261
|
+
/** Group DM recipients as User objects. Empty for 1:1 DMs. */
|
|
1262
|
+
recipients;
|
|
1263
|
+
/** Group DM member display names (userId -> nickname). */
|
|
1264
|
+
nicks;
|
|
1265
|
+
constructor(client, data) {
|
|
1266
|
+
super(client, data);
|
|
1267
|
+
this.lastMessageId = data.last_message_id ?? null;
|
|
1268
|
+
this.ownerId = data.owner_id ?? null;
|
|
1269
|
+
this.recipients = (data.recipients ?? []).map(
|
|
1270
|
+
(u) => client.getOrCreateUser(u)
|
|
1271
|
+
);
|
|
1272
|
+
this.nicks = data.nicks ?? {};
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Send a message to this DM channel.
|
|
1276
|
+
* @param options - Text content or object with content, embeds, and/or files
|
|
1277
|
+
*/
|
|
1278
|
+
async send(options) {
|
|
1279
|
+
const opts = typeof options === "string" ? { content: options } : options;
|
|
1280
|
+
const body = buildSendBody(options);
|
|
1281
|
+
const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
|
|
1282
|
+
const postOptions = files?.length ? { body, files } : { body };
|
|
1283
|
+
const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
|
|
1284
|
+
this.client._addMessageToCache(this.id, data);
|
|
1285
|
+
return new Message(this.client, data);
|
|
1286
|
+
}
|
|
1287
|
+
/** Message manager for this channel. Use channel.messages.fetch(messageId). */
|
|
1288
|
+
get messages() {
|
|
1289
|
+
return new MessageManager(this.client, this.id);
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Create a message collector for this DM channel.
|
|
1293
|
+
* @param options - Filter, time (ms), and max count
|
|
1294
|
+
*/
|
|
1295
|
+
createMessageCollector(options) {
|
|
1296
|
+
return new MessageCollector(this.client, this.id, options);
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Fetch pinned messages in this DM channel.
|
|
1300
|
+
* @returns Pinned messages
|
|
1301
|
+
*/
|
|
1302
|
+
async fetchPinnedMessages() {
|
|
1303
|
+
const data = await this.client.rest.get(Routes5.channelPins(this.id));
|
|
1304
|
+
const list = Array.isArray(data) ? data : data?.items ?? [];
|
|
1305
|
+
return list.map((item) => {
|
|
1306
|
+
const msg = typeof item === "object" && item && "message" in item ? item.message : item;
|
|
1307
|
+
return new Message(this.client, msg);
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Fetch a message by ID from this DM channel.
|
|
1312
|
+
* @param messageId - Snowflake of the message
|
|
1313
|
+
* @returns The message, or null if not found
|
|
1314
|
+
* @deprecated Use channel.messages.fetch(messageId) instead.
|
|
1315
|
+
*/
|
|
1316
|
+
async fetchMessage(messageId) {
|
|
1317
|
+
emitDeprecationWarning(
|
|
1318
|
+
"Channel.fetchMessage()",
|
|
1319
|
+
"Use channel.messages.fetch(messageId) instead."
|
|
1320
|
+
);
|
|
1321
|
+
return this.client.channels.fetchMessage(this.id, messageId);
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Add a recipient to this Group DM. Requires Group DM (type GroupDM).
|
|
1325
|
+
* PUT /channels/{id}/recipients/{userId}.
|
|
1326
|
+
*/
|
|
1327
|
+
async addRecipient(userId) {
|
|
1328
|
+
await this.client.rest.put(Routes5.channelRecipient(this.id, userId), { auth: true });
|
|
1329
|
+
const user = this.client.users.get(userId) ?? await this.client.users.fetch(userId);
|
|
1330
|
+
if (user) this.recipients.push(user);
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Remove a recipient from this Group DM. Requires Group DM (type GroupDM).
|
|
1334
|
+
* DELETE /channels/{id}/recipients/{userId}.
|
|
1335
|
+
* @param options - silent: if true, does not send a system message (default false)
|
|
1336
|
+
*/
|
|
1337
|
+
async removeRecipient(userId, options) {
|
|
1338
|
+
const url = Routes5.channelRecipient(this.id, userId) + (options?.silent ? "?silent=true" : "");
|
|
1339
|
+
await this.client.rest.delete(url, { auth: true });
|
|
1340
|
+
const idx = this.recipients.findIndex((u) => u.id === userId);
|
|
1341
|
+
if (idx >= 0) this.recipients.splice(idx, 1);
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
// src/client/ChannelManager.ts
|
|
1346
|
+
var ChannelManager = class extends Collection4 {
|
|
1347
|
+
constructor(client) {
|
|
1348
|
+
super();
|
|
1349
|
+
this.client = client;
|
|
1350
|
+
this.maxSize = client.options?.cache?.channels ?? 0;
|
|
1351
|
+
}
|
|
1352
|
+
maxSize;
|
|
1353
|
+
set(key, value) {
|
|
1354
|
+
if (this.maxSize > 0 && this.size >= this.maxSize && !this.has(key)) {
|
|
1355
|
+
const firstKey = this.keys().next().value;
|
|
1356
|
+
if (firstKey !== void 0) this.delete(firstKey);
|
|
1357
|
+
}
|
|
1358
|
+
return super.set(key, value);
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Get a channel from cache or fetch from the API if not present.
|
|
1362
|
+
* Convenience helper to avoid repeating `client.channels.get(id) ?? (await client.channels.fetch(id))`.
|
|
1363
|
+
* @param channelId - Snowflake of the channel
|
|
1364
|
+
* @returns The channel
|
|
1365
|
+
* @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
|
|
1366
|
+
* @example
|
|
1367
|
+
* const channel = await client.channels.resolve(message.channelId);
|
|
1368
|
+
* if (channel?.isTextBased()) await channel.send('Hello!');
|
|
1369
|
+
*/
|
|
1370
|
+
async resolve(channelId) {
|
|
1371
|
+
return this.get(channelId) ?? this.fetch(channelId);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Fetch a channel by ID from the API (or return from cache if present).
|
|
1375
|
+
* @param channelId - Snowflake of the channel
|
|
1376
|
+
* @returns The channel
|
|
1377
|
+
* @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
|
|
1378
|
+
* @example
|
|
1379
|
+
* const channel = await client.channels.fetch(channelId);
|
|
1380
|
+
* if (channel?.isTextBased()) await channel.send('Hello!');
|
|
1381
|
+
*/
|
|
1382
|
+
async fetch(channelId) {
|
|
1383
|
+
const cached = this.get(channelId);
|
|
1384
|
+
if (cached) return cached;
|
|
1385
|
+
try {
|
|
1386
|
+
const data = await this.client.rest.get(Routes6.channel(channelId));
|
|
1387
|
+
const channel = Channel.fromOrCreate(this.client, data);
|
|
1388
|
+
if (!channel) {
|
|
1389
|
+
throw new FluxerError("Channel data invalid or unsupported type", {
|
|
1390
|
+
code: ErrorCodes.ChannelNotFound
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
this.set(channel.id, channel);
|
|
1394
|
+
if ("guildId" in channel) {
|
|
1395
|
+
const guild = this.client.guilds.get(channel.guildId);
|
|
1396
|
+
if (guild) guild.channels.set(channel.id, channel);
|
|
1397
|
+
}
|
|
1398
|
+
return channel;
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
if (err instanceof RateLimitError2) throw err;
|
|
1401
|
+
if (err instanceof FluxerAPIError2 && err.statusCode === 404) {
|
|
1402
|
+
throw new FluxerError(`Channel ${channelId} not found`, {
|
|
1403
|
+
code: ErrorCodes.ChannelNotFound,
|
|
1404
|
+
cause: err
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Fetch a message by ID from the API.
|
|
1412
|
+
* @param channelId - Snowflake of the channel
|
|
1413
|
+
* @param messageId - Snowflake of the message
|
|
1414
|
+
* @returns The message
|
|
1415
|
+
* @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
|
|
1416
|
+
* @deprecated Use channel.messages.fetch(messageId). Prefer (await client.channels.resolve(channelId))?.messages?.fetch(messageId).
|
|
1417
|
+
* @example
|
|
1418
|
+
* const channel = await client.channels.resolve(channelId);
|
|
1419
|
+
* const message = await channel?.messages?.fetch(messageId);
|
|
1420
|
+
*/
|
|
1421
|
+
async fetchMessage(channelId, messageId) {
|
|
1422
|
+
emitDeprecationWarning2(
|
|
1423
|
+
"ChannelManager.fetchMessage()",
|
|
1424
|
+
"Use channel.messages.fetch(messageId). Prefer (await client.channels.resolve(channelId))?.messages?.fetch(messageId)."
|
|
1425
|
+
);
|
|
1426
|
+
try {
|
|
1427
|
+
const data = await this.client.rest.get(
|
|
1428
|
+
Routes6.channelMessage(channelId, messageId)
|
|
1429
|
+
);
|
|
1430
|
+
return new Message(this.client, data);
|
|
1431
|
+
} catch (err) {
|
|
1432
|
+
if (err instanceof RateLimitError2) throw err;
|
|
1433
|
+
if (err instanceof FluxerAPIError2 && err.statusCode === 404) {
|
|
1434
|
+
throw new FluxerError(`Message ${messageId} not found in channel ${channelId}`, {
|
|
1435
|
+
code: ErrorCodes.MessageNotFound,
|
|
1436
|
+
cause: err
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Send a message to a channel by ID. Works even when the channel is not cached.
|
|
1444
|
+
* Skips the fetch when you only need to send.
|
|
1445
|
+
* @param channelId - Snowflake of the channel (text channel or DM)
|
|
1446
|
+
* @param payload - Text content or object with content, embeds, and/or files
|
|
1447
|
+
* @returns The created message
|
|
1448
|
+
* @example
|
|
1449
|
+
* await client.channels.send(logChannelId, 'User joined!');
|
|
1450
|
+
* await client.channels.send(channelId, { embeds: [embed] });
|
|
1451
|
+
* await client.channels.send(channelId, { content: 'Report', files: [{ name: 'log.txt', data }] });
|
|
1452
|
+
*/
|
|
1453
|
+
async send(channelId, payload) {
|
|
1454
|
+
const opts = typeof payload === "string" ? { content: payload } : payload;
|
|
1455
|
+
const body = buildSendBody(payload);
|
|
1456
|
+
const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
|
|
1457
|
+
const postOptions = files?.length ? { body, files } : { body };
|
|
1458
|
+
const data = await this.client.rest.post(Routes6.channelMessages(channelId), postOptions);
|
|
1459
|
+
this.client._addMessageToCache(channelId, data);
|
|
1460
|
+
return new Message(this.client, data);
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
// src/client/GuildManager.ts
|
|
1465
|
+
import { Collection as Collection8 } from "@erinjs/collection";
|
|
1466
|
+
import { Routes as Routes15 } from "@erinjs/types";
|
|
1467
|
+
|
|
1468
|
+
// src/structures/Guild.ts
|
|
1469
|
+
import {
|
|
1470
|
+
parseRoleMention,
|
|
1471
|
+
resolvePermissionsToBitfield as resolvePermissionsToBitfield2
|
|
1472
|
+
} from "@erinjs/util";
|
|
1473
|
+
import { FluxerAPIError as FluxerAPIError3 } from "@erinjs/rest";
|
|
1474
|
+
import { Collection as Collection7 } from "@erinjs/collection";
|
|
1475
|
+
|
|
1476
|
+
// src/client/GuildMemberManager.ts
|
|
1477
|
+
import { Collection as Collection6 } from "@erinjs/collection";
|
|
1478
|
+
import { Routes as Routes9 } from "@erinjs/types";
|
|
1479
|
+
|
|
1480
|
+
// src/structures/GuildMember.ts
|
|
1481
|
+
import { BitField } from "@erinjs/util";
|
|
1482
|
+
import { Routes as Routes8 } from "@erinjs/types";
|
|
1483
|
+
|
|
1484
|
+
// src/util/permissions.ts
|
|
1485
|
+
import { OverwriteType } from "@erinjs/types";
|
|
1486
|
+
import { ALL_PERMISSIONS_BIGINT, PermissionFlags as PermissionFlags2 } from "@erinjs/util";
|
|
1487
|
+
function computePermissions(basePermissions, overwrites, memberRoles, memberId, isOwner) {
|
|
1488
|
+
if (isOwner) return ALL_PERMISSIONS_BIGINT;
|
|
1489
|
+
let perms = basePermissions;
|
|
1490
|
+
for (const overwrite of overwrites ?? []) {
|
|
1491
|
+
const applies = overwrite.type === OverwriteType.Role && memberRoles.includes(overwrite.id) || overwrite.type === OverwriteType.Member && overwrite.id === memberId;
|
|
1492
|
+
if (!applies) continue;
|
|
1493
|
+
const allow = BigInt(overwrite.allow || "0");
|
|
1494
|
+
const deny = BigInt(overwrite.deny || "0");
|
|
1495
|
+
perms = perms & ~deny | allow;
|
|
1496
|
+
}
|
|
1497
|
+
return (perms & PermissionFlags2.Administrator) !== 0n ? ALL_PERMISSIONS_BIGINT : perms;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// src/structures/GuildMemberRoleManager.ts
|
|
1501
|
+
import { Collection as Collection5 } from "@erinjs/collection";
|
|
1502
|
+
import { Routes as Routes7 } from "@erinjs/types";
|
|
1503
|
+
var GuildMemberRoleManager = class {
|
|
1504
|
+
constructor(member, initialRoleIds = []) {
|
|
1505
|
+
this.member = member;
|
|
1506
|
+
this._roleIds = [...initialRoleIds];
|
|
1507
|
+
}
|
|
1508
|
+
_roleIds = [];
|
|
1509
|
+
/** Role IDs for this member. Used by permissions; prefer cache for Role objects. */
|
|
1510
|
+
get roleIds() {
|
|
1511
|
+
return this._roleIds;
|
|
1512
|
+
}
|
|
1513
|
+
/** Check if the member has a role. Discord.js parity: member.roles.cache.has(roleId) */
|
|
1514
|
+
has(roleOrId) {
|
|
1515
|
+
return this._roleIds.includes(this._resolveId(roleOrId));
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Collection of Role objects for this member's roles (from guild.roles).
|
|
1519
|
+
* Discord.js parity: member.roles.cache
|
|
1520
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberRoleManager
|
|
1521
|
+
*/
|
|
1522
|
+
get cache() {
|
|
1523
|
+
const coll = new Collection5();
|
|
1524
|
+
for (const id of this._roleIds) {
|
|
1525
|
+
const role = this.member.guild.roles.get(id);
|
|
1526
|
+
if (role) coll.set(id, role);
|
|
1527
|
+
}
|
|
1528
|
+
return coll;
|
|
1529
|
+
}
|
|
1530
|
+
/** Resolve role ID from RoleResolvable. */
|
|
1531
|
+
_resolveId(roleOrId) {
|
|
1532
|
+
return typeof roleOrId === "string" ? roleOrId : roleOrId.id;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Add a role to this member.
|
|
1536
|
+
* Discord.js parity: member.roles.add(roleId)
|
|
1537
|
+
* Requires Manage Roles permission.
|
|
1538
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberRoleManager
|
|
1539
|
+
*/
|
|
1540
|
+
async add(roleOrId) {
|
|
1541
|
+
const roleId = this._resolveId(roleOrId);
|
|
1542
|
+
if (this._roleIds.includes(roleId)) return;
|
|
1543
|
+
await this.member.client.rest.put(
|
|
1544
|
+
Routes7.guildMemberRole(this.member.guild.id, this.member.id, roleId)
|
|
1545
|
+
);
|
|
1546
|
+
this._roleIds.push(roleId);
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Remove a role from this member.
|
|
1550
|
+
* Discord.js parity: member.roles.remove(roleId)
|
|
1551
|
+
* Requires Manage Roles permission.
|
|
1552
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberRoleManager
|
|
1553
|
+
*/
|
|
1554
|
+
async remove(roleOrId) {
|
|
1555
|
+
const roleId = this._resolveId(roleOrId);
|
|
1556
|
+
const idx = this._roleIds.indexOf(roleId);
|
|
1557
|
+
if (idx === -1) return;
|
|
1558
|
+
await this.member.client.rest.delete(
|
|
1559
|
+
Routes7.guildMemberRole(this.member.guild.id, this.member.id, roleId)
|
|
1560
|
+
);
|
|
1561
|
+
this._roleIds.splice(idx, 1);
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Replace all roles for this member. PATCH /guilds/{id}/members/{userId}
|
|
1565
|
+
* Discord.js parity: member.roles.set(roleIds)
|
|
1566
|
+
* Requires Manage Roles permission.
|
|
1567
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberRoleManager
|
|
1568
|
+
*/
|
|
1569
|
+
async set(roleIds) {
|
|
1570
|
+
const data = await this.member.client.rest.patch(
|
|
1571
|
+
Routes7.guildMember(this.member.guild.id, this.member.id),
|
|
1572
|
+
{ body: { roles: roleIds }, auth: true }
|
|
1573
|
+
);
|
|
1574
|
+
this._roleIds = data.roles ? [...data.roles] : [];
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Update internal role IDs from API response. Called by GuildMember.edit().
|
|
1578
|
+
* @internal
|
|
1579
|
+
*/
|
|
1580
|
+
_patch(roleIds) {
|
|
1581
|
+
this._roleIds = [...roleIds];
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1585
|
+
// src/structures/GuildMember.ts
|
|
1586
|
+
var GuildMember = class extends Base {
|
|
1587
|
+
client;
|
|
1588
|
+
id;
|
|
1589
|
+
user;
|
|
1590
|
+
guild;
|
|
1591
|
+
nick;
|
|
1592
|
+
/**
|
|
1593
|
+
* Role manager with add/remove/set and cache. Discord.js parity: member.roles.add(), member.roles.cache
|
|
1594
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberRoleManager
|
|
1595
|
+
*/
|
|
1596
|
+
roles;
|
|
1597
|
+
joinedAt;
|
|
1598
|
+
communicationDisabledUntil;
|
|
1599
|
+
mute;
|
|
1600
|
+
deaf;
|
|
1601
|
+
avatar;
|
|
1602
|
+
banner;
|
|
1603
|
+
accentColor;
|
|
1604
|
+
profileFlags;
|
|
1605
|
+
/** @param data - API guild member from GET /guilds/{id}/members or GET /guilds/{id}/members/{user_id} */
|
|
1606
|
+
constructor(client, data, guild) {
|
|
1607
|
+
super();
|
|
1608
|
+
this.client = client;
|
|
1609
|
+
this.user = client.getOrCreateUser(data.user);
|
|
1610
|
+
this.id = data.user.id;
|
|
1611
|
+
this.guild = guild;
|
|
1612
|
+
this.nick = data.nick ?? null;
|
|
1613
|
+
this.roles = new GuildMemberRoleManager(this, data.roles ?? []);
|
|
1614
|
+
this.joinedAt = new Date(data.joined_at);
|
|
1615
|
+
this.communicationDisabledUntil = data.communication_disabled_until ? new Date(data.communication_disabled_until) : null;
|
|
1616
|
+
this.mute = data.mute ?? false;
|
|
1617
|
+
this.deaf = data.deaf ?? false;
|
|
1618
|
+
this.avatar = data.avatar ?? null;
|
|
1619
|
+
this.banner = data.banner ?? null;
|
|
1620
|
+
this.accentColor = data.accent_color ?? null;
|
|
1621
|
+
this.profileFlags = data.profile_flags ?? null;
|
|
1622
|
+
}
|
|
1623
|
+
/** Nickname, or global name, or username. */
|
|
1624
|
+
get displayName() {
|
|
1625
|
+
return this.nick ?? this.user.globalName ?? this.user.username;
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Get the guild-specific avatar URL for this member.
|
|
1629
|
+
* Returns null if the member has no guild avatar (use displayAvatarURL for fallback).
|
|
1630
|
+
*/
|
|
1631
|
+
avatarURL(options) {
|
|
1632
|
+
return cdnMemberAvatarURL(this.guild.id, this.id, this.avatar, options);
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Get the avatar URL to display for this member.
|
|
1636
|
+
* Uses guild-specific avatar if set, otherwise falls back to the user's avatar.
|
|
1637
|
+
*/
|
|
1638
|
+
displayAvatarURL(options) {
|
|
1639
|
+
return this.avatarURL(options) ?? this.user.displayAvatarURL(options);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Get the guild-specific banner URL for this member.
|
|
1643
|
+
* Returns null if the member has no guild banner.
|
|
1644
|
+
*/
|
|
1645
|
+
bannerURL(options) {
|
|
1646
|
+
return cdnMemberBannerURL(this.guild.id, this.id, this.banner, options);
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Add a role to this member.
|
|
1650
|
+
* Prefer member.roles.add(roleId) for Discord.js parity.
|
|
1651
|
+
* @param roleId - The role ID to add
|
|
1652
|
+
* Requires Manage Roles permission.
|
|
1653
|
+
*/
|
|
1654
|
+
async addRole(roleId) {
|
|
1655
|
+
await this.roles.add(roleId);
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Remove a role from this member.
|
|
1659
|
+
* Prefer member.roles.remove(roleId) for Discord.js parity.
|
|
1660
|
+
* @param roleId - The role ID to remove
|
|
1661
|
+
* Requires Manage Roles permission.
|
|
1662
|
+
*/
|
|
1663
|
+
async removeRole(roleId) {
|
|
1664
|
+
await this.roles.remove(roleId);
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Edit this guild member. PATCH /guilds/{id}/members/{userId} or /members/@me for the bot.
|
|
1668
|
+
* For @me: nick, avatar, banner, bio, pronouns, accent_color, profile_flags, mute, deaf,
|
|
1669
|
+
* communication_disabled_until, timeout_reason, channel_id, connection_id.
|
|
1670
|
+
* For other members: same plus roles (array of role IDs).
|
|
1671
|
+
*/
|
|
1672
|
+
async edit(options) {
|
|
1673
|
+
const isMe = this.client.user?.id === this.id;
|
|
1674
|
+
const route = isMe ? `/guilds/${this.guild.id}/members/@me` : Routes8.guildMember(this.guild.id, this.id);
|
|
1675
|
+
const data = await this.client.rest.patch(route, {
|
|
1676
|
+
body: options,
|
|
1677
|
+
auth: true
|
|
1678
|
+
});
|
|
1679
|
+
this.nick = data.nick ?? this.nick;
|
|
1680
|
+
if (data.roles) this.roles._patch(data.roles);
|
|
1681
|
+
if (data.communication_disabled_until != null) {
|
|
1682
|
+
this.communicationDisabledUntil = data.communication_disabled_until ? new Date(data.communication_disabled_until) : null;
|
|
1683
|
+
}
|
|
1684
|
+
return this;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Get the member's guild-level permissions (from roles only, no channel overwrites).
|
|
1688
|
+
* Use this for server-wide permission checks (e.g. ban, kick, manage roles).
|
|
1689
|
+
* @returns Object with has(permission) to check specific permissions
|
|
1690
|
+
* @example
|
|
1691
|
+
* const perms = member.permissions;
|
|
1692
|
+
* if (perms.has(PermissionFlags.BanMembers)) { ... }
|
|
1693
|
+
*/
|
|
1694
|
+
get permissions() {
|
|
1695
|
+
const base = this._computeBasePermissions();
|
|
1696
|
+
const ownerId = this.guild.ownerId;
|
|
1697
|
+
const isOwner = ownerId != null && ownerId !== "" && String(ownerId) === String(this.id);
|
|
1698
|
+
const perms = computePermissions(base, [], [], this.id, isOwner);
|
|
1699
|
+
return new BitField(perms);
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Compute the member's effective permissions in a guild channel.
|
|
1703
|
+
* Applies role permissions and channel overwrites.
|
|
1704
|
+
* @param channel - The guild channel to check permissions for
|
|
1705
|
+
* @returns Object with has(permission) to check specific permissions
|
|
1706
|
+
* @example
|
|
1707
|
+
* const perms = member.permissionsIn(channel);
|
|
1708
|
+
* if (perms.has(PermissionFlags.SendMessages)) { ... }
|
|
1709
|
+
*/
|
|
1710
|
+
permissionsIn(channel) {
|
|
1711
|
+
const base = this._computeBasePermissions();
|
|
1712
|
+
const ownerId = this.guild.ownerId;
|
|
1713
|
+
const isOwner = ownerId != null && ownerId !== "" && String(ownerId) === String(this.id);
|
|
1714
|
+
const perms = computePermissions(
|
|
1715
|
+
base,
|
|
1716
|
+
channel.permissionOverwrites,
|
|
1717
|
+
[...this.roles.roleIds],
|
|
1718
|
+
this.id,
|
|
1719
|
+
isOwner
|
|
1720
|
+
);
|
|
1721
|
+
return new BitField(perms);
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Move this member to a different voice channel or disconnect them from voice.
|
|
1725
|
+
* Requires Move Members permission.
|
|
1726
|
+
* @param channelId - The voice channel ID to move the member to, or null to disconnect
|
|
1727
|
+
* @param connectionId - Optional connection ID for the specific voice session (usually not needed)
|
|
1728
|
+
* @returns Promise that resolves when the member has been moved
|
|
1729
|
+
* @example
|
|
1730
|
+
* // Move member to a different voice channel
|
|
1731
|
+
* await member.move('123456789012345678');
|
|
1732
|
+
*
|
|
1733
|
+
* // Disconnect member from voice
|
|
1734
|
+
* await member.move(null);
|
|
1735
|
+
*/
|
|
1736
|
+
async move(channelId, connectionId) {
|
|
1737
|
+
await this.edit({
|
|
1738
|
+
channel_id: channelId,
|
|
1739
|
+
connection_id: connectionId
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
_computeBasePermissions() {
|
|
1743
|
+
let base = 0n;
|
|
1744
|
+
const everyone = this.guild.roles.get(this.guild.id);
|
|
1745
|
+
if (everyone) base |= everyone.permissions.bitfield;
|
|
1746
|
+
for (const roleId of this.roles.roleIds) {
|
|
1747
|
+
if (roleId === this.guild.id) continue;
|
|
1748
|
+
const role = this.guild.roles.get(roleId);
|
|
1749
|
+
if (role) base |= role.permissions.bitfield;
|
|
1750
|
+
}
|
|
1751
|
+
return base;
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
|
|
1755
|
+
// src/client/GuildMemberManager.ts
|
|
1756
|
+
var GuildMemberManager = class extends Collection6 {
|
|
1757
|
+
constructor(guild) {
|
|
1758
|
+
super();
|
|
1759
|
+
this.guild = guild;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Get a guild member from cache or fetch from the API if not present.
|
|
1763
|
+
* Convenience helper to avoid repeating `guild.members.get(userId) ?? (await guild.fetchMember(userId))`.
|
|
1764
|
+
* @param userId - Snowflake of the user
|
|
1765
|
+
* @returns The guild member
|
|
1766
|
+
* @throws FluxerError with MEMBER_NOT_FOUND if user is not in the guild (404)
|
|
1767
|
+
* @example
|
|
1768
|
+
* const member = await guild.members.resolve(userId);
|
|
1769
|
+
* console.log(member.displayName);
|
|
1770
|
+
*/
|
|
1771
|
+
async resolve(userId) {
|
|
1772
|
+
return this.get(userId) ?? this.guild.fetchMember(userId);
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* The current bot user as a GuildMember in this guild.
|
|
1776
|
+
* Returns null if the bot's member is not cached or client.user is null.
|
|
1777
|
+
* Use fetchMe() to load the bot's member when not cached.
|
|
1778
|
+
*
|
|
1779
|
+
* @discordJsCompat https://discord.js.org/docs/packages/discord.js/main/GuildMemberManager
|
|
1780
|
+
* @example
|
|
1781
|
+
* const perms = guild.members.me?.permissions;
|
|
1782
|
+
* if (perms?.has(PermissionFlags.BanMembers)) { ... }
|
|
1783
|
+
*/
|
|
1784
|
+
get me() {
|
|
1785
|
+
const userId = this.guild.client.user?.id;
|
|
1786
|
+
return userId ? this.get(userId) ?? null : null;
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Fetch the current bot user as a GuildMember in this guild.
|
|
1790
|
+
* Caches the result in guild.members.
|
|
1791
|
+
*
|
|
1792
|
+
* @throws Error if client.user is null (client not ready)
|
|
1793
|
+
* @example
|
|
1794
|
+
* const me = await guild.members.fetchMe();
|
|
1795
|
+
* console.log(me.displayName);
|
|
1796
|
+
*/
|
|
1797
|
+
async fetchMe() {
|
|
1798
|
+
const userId = this.guild.client.user?.id;
|
|
1799
|
+
if (!userId) {
|
|
1800
|
+
throw new Error("Cannot fetch me: client.user is null (client not ready)");
|
|
1801
|
+
}
|
|
1802
|
+
return this.guild.fetchMember(userId);
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Fetch guild members with pagination. GET /guilds/{id}/members.
|
|
1806
|
+
* @param options - limit (1-1000), after (user ID for pagination)
|
|
1807
|
+
* @returns Array of GuildMember objects (cached in guild.members)
|
|
1808
|
+
*/
|
|
1809
|
+
async fetch(options) {
|
|
1810
|
+
const params = new URLSearchParams();
|
|
1811
|
+
if (options?.limit != null) params.set("limit", String(options.limit));
|
|
1812
|
+
if (options?.after) params.set("after", options.after);
|
|
1813
|
+
const qs = params.toString();
|
|
1814
|
+
const url = Routes9.guildMembers(this.guild.id) + (qs ? `?${qs}` : "");
|
|
1815
|
+
const data = await this.guild.client.rest.get(url, { auth: true });
|
|
1816
|
+
const list = Array.isArray(data) ? data : data?.members ?? [];
|
|
1817
|
+
const members = [];
|
|
1818
|
+
for (const m of list) {
|
|
1819
|
+
const member = new GuildMember(
|
|
1820
|
+
this.guild.client,
|
|
1821
|
+
{ ...m, guild_id: this.guild.id },
|
|
1822
|
+
this.guild
|
|
1823
|
+
);
|
|
1824
|
+
this.set(member.id, member);
|
|
1825
|
+
members.push(member);
|
|
1826
|
+
}
|
|
1827
|
+
return members;
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
|
|
1831
|
+
// src/structures/Role.ts
|
|
1832
|
+
import { Routes as Routes10 } from "@erinjs/types";
|
|
1833
|
+
import {
|
|
1834
|
+
PermissionFlags as PermissionFlags4,
|
|
1835
|
+
resolvePermissionsToBitfield,
|
|
1836
|
+
ALL_PERMISSIONS_BIGINT as ALL_PERMISSIONS_BIGINT2,
|
|
1837
|
+
PermissionsBitField
|
|
1838
|
+
} from "@erinjs/util";
|
|
1839
|
+
var Role = class extends Base {
|
|
1840
|
+
client;
|
|
1841
|
+
id;
|
|
1842
|
+
guildId;
|
|
1843
|
+
name;
|
|
1844
|
+
color;
|
|
1845
|
+
position;
|
|
1846
|
+
_permissions;
|
|
1847
|
+
hoist;
|
|
1848
|
+
mentionable;
|
|
1849
|
+
unicodeEmoji;
|
|
1850
|
+
/** Separately sorted position for hoisted roles. Null if not set. */
|
|
1851
|
+
hoistPosition;
|
|
1852
|
+
/** @param client - The client instance */
|
|
1853
|
+
/** @param data - API role from GET /guilds/{id}/roles or gateway role events */
|
|
1854
|
+
/** @param guildId - The guild this role belongs to */
|
|
1855
|
+
constructor(client, data, guildId) {
|
|
1856
|
+
super();
|
|
1857
|
+
this.client = client;
|
|
1858
|
+
this.id = data.id;
|
|
1859
|
+
this.guildId = guildId;
|
|
1860
|
+
this.name = data.name;
|
|
1861
|
+
this.color = data.color;
|
|
1862
|
+
this.position = data.position;
|
|
1863
|
+
this._permissions = data.permissions;
|
|
1864
|
+
this.hoist = !!data.hoist;
|
|
1865
|
+
this.mentionable = !!data.mentionable;
|
|
1866
|
+
this.unicodeEmoji = data.unicode_emoji ?? null;
|
|
1867
|
+
this.hoistPosition = data.hoist_position ?? null;
|
|
1868
|
+
}
|
|
1869
|
+
get permissions() {
|
|
1870
|
+
const bits = BigInt(this._permissions);
|
|
1871
|
+
return new PermissionsBitField(
|
|
1872
|
+
(bits & PermissionFlags4.Administrator) !== 0n ? ALL_PERMISSIONS_BIGINT2 : bits
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
/** Update mutable fields from fresh API data. Used by edit and gateway events. */
|
|
1876
|
+
_patch(data) {
|
|
1877
|
+
if (data.name !== void 0) this.name = data.name;
|
|
1878
|
+
if (data.color !== void 0) this.color = data.color;
|
|
1879
|
+
if (data.position !== void 0) this.position = data.position;
|
|
1880
|
+
if (data.permissions !== void 0) this._permissions = data.permissions;
|
|
1881
|
+
if (data.hoist !== void 0) this.hoist = !!data.hoist;
|
|
1882
|
+
if (data.mentionable !== void 0) this.mentionable = !!data.mentionable;
|
|
1883
|
+
if (data.unicode_emoji !== void 0) this.unicodeEmoji = data.unicode_emoji ?? null;
|
|
1884
|
+
if (data.hoist_position !== void 0) this.hoistPosition = data.hoist_position ?? null;
|
|
1885
|
+
}
|
|
1886
|
+
/** Returns a mention string (e.g. `<@&123456>`). */
|
|
1887
|
+
toString() {
|
|
1888
|
+
return `<@&${this.id}>`;
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Edit this role.
|
|
1892
|
+
* Requires Manage Roles permission.
|
|
1893
|
+
* @param options - Role updates (permissions accepts PermissionResolvable for convenience)
|
|
1894
|
+
* @returns This role (updated in place)
|
|
1895
|
+
* @example
|
|
1896
|
+
* await role.edit({ name: 'Moderator', permissions: ['BanMembers', 'KickMembers'] });
|
|
1897
|
+
*/
|
|
1898
|
+
async edit(options) {
|
|
1899
|
+
const body = {};
|
|
1900
|
+
if (options.name !== void 0) body.name = options.name;
|
|
1901
|
+
if (options.permissions !== void 0) {
|
|
1902
|
+
body.permissions = typeof options.permissions === "string" ? options.permissions : resolvePermissionsToBitfield(options.permissions);
|
|
1903
|
+
}
|
|
1904
|
+
if (options.color !== void 0) body.color = options.color;
|
|
1905
|
+
if (options.hoist !== void 0) body.hoist = options.hoist;
|
|
1906
|
+
if (options.mentionable !== void 0) body.mentionable = options.mentionable;
|
|
1907
|
+
if (options.unicode_emoji !== void 0) body.unicode_emoji = options.unicode_emoji;
|
|
1908
|
+
if (options.position !== void 0) body.position = options.position;
|
|
1909
|
+
if (options.hoist_position !== void 0) body.hoist_position = options.hoist_position;
|
|
1910
|
+
const data = await this.client.rest.patch(Routes10.guildRole(this.guildId, this.id), {
|
|
1911
|
+
body: Object.keys(body).length ? body : void 0,
|
|
1912
|
+
auth: true
|
|
1913
|
+
});
|
|
1914
|
+
this._patch(data);
|
|
1915
|
+
return this;
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Delete this role.
|
|
1919
|
+
* Requires Manage Roles permission.
|
|
1920
|
+
*/
|
|
1921
|
+
async delete() {
|
|
1922
|
+
await this.client.rest.delete(Routes10.guildRole(this.guildId, this.id), { auth: true });
|
|
1923
|
+
const guild = this.client.guilds.get(this.guildId);
|
|
1924
|
+
if (guild) guild.roles.delete(this.id);
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
// src/structures/Guild.ts
|
|
1929
|
+
import { Routes as Routes14 } from "@erinjs/types";
|
|
1930
|
+
|
|
1931
|
+
// src/structures/GuildBan.ts
|
|
1932
|
+
import { Routes as Routes11 } from "@erinjs/types";
|
|
1933
|
+
var GuildBan = class extends Base {
|
|
1934
|
+
client;
|
|
1935
|
+
guildId;
|
|
1936
|
+
user;
|
|
1937
|
+
reason;
|
|
1938
|
+
/** ISO timestamp when a temporary ban expires. Null for permanent bans. */
|
|
1939
|
+
expiresAt;
|
|
1940
|
+
/** @param data - API ban from GET /guilds/{id}/bans or gateway GUILD_BAN_ADD */
|
|
1941
|
+
constructor(client, data, guildId) {
|
|
1942
|
+
super();
|
|
1943
|
+
this.client = client;
|
|
1944
|
+
this.guildId = data.guild_id ?? guildId;
|
|
1945
|
+
this.user = client.getOrCreateUser(data.user);
|
|
1946
|
+
this.reason = data.reason ?? null;
|
|
1947
|
+
this.expiresAt = data.expires_at ?? null;
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Remove this ban (unban the user).
|
|
1951
|
+
* Requires Ban Members permission.
|
|
1952
|
+
*/
|
|
1953
|
+
async unban() {
|
|
1954
|
+
await this.client.rest.delete(Routes11.guildBan(this.guildId, this.user.id), {
|
|
1955
|
+
auth: true
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
|
|
1960
|
+
// src/structures/GuildEmoji.ts
|
|
1961
|
+
import { Routes as Routes12 } from "@erinjs/types";
|
|
1962
|
+
var GuildEmoji = class extends Base {
|
|
1963
|
+
client;
|
|
1964
|
+
id;
|
|
1965
|
+
guildId;
|
|
1966
|
+
name;
|
|
1967
|
+
animated;
|
|
1968
|
+
/** @param data - API emoji from GET /guilds/{id}/emojis or guild emoji events */
|
|
1969
|
+
constructor(client, data, guildId) {
|
|
1970
|
+
super();
|
|
1971
|
+
this.client = client;
|
|
1972
|
+
this.id = data.id;
|
|
1973
|
+
this.guildId = data.guild_id ?? guildId;
|
|
1974
|
+
this.name = data.name;
|
|
1975
|
+
this.animated = data.animated ?? false;
|
|
1976
|
+
}
|
|
1977
|
+
/** CDN URL for this emoji image. */
|
|
1978
|
+
get url() {
|
|
1979
|
+
const ext = this.animated ? "gif" : "png";
|
|
1980
|
+
return `${CDN_URL}/emojis/${this.id}.${ext}`;
|
|
1981
|
+
}
|
|
1982
|
+
/** Emoji identifier for use in reactions: `name:id` */
|
|
1983
|
+
get identifier() {
|
|
1984
|
+
return `${this.name}:${this.id}`;
|
|
1985
|
+
}
|
|
1986
|
+
/** Delete this emoji. Requires Manage Emojis and Stickers permission. */
|
|
1987
|
+
async delete() {
|
|
1988
|
+
await this.client.rest.delete(Routes12.guildEmoji(this.guildId, this.id), {
|
|
1989
|
+
auth: true
|
|
1990
|
+
});
|
|
1991
|
+
const guild = this.client.guilds.get(this.guildId);
|
|
1992
|
+
if (guild) guild.emojis.delete(this.id);
|
|
1993
|
+
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Edit this emoji's name.
|
|
1996
|
+
* Requires Manage Emojis and Stickers permission.
|
|
1997
|
+
*/
|
|
1998
|
+
async edit(options) {
|
|
1999
|
+
const data = await this.client.rest.patch(Routes12.guildEmoji(this.guildId, this.id), {
|
|
2000
|
+
body: options,
|
|
2001
|
+
auth: true
|
|
2002
|
+
});
|
|
2003
|
+
this.name = data.name;
|
|
2004
|
+
return this;
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
|
|
2008
|
+
// src/structures/GuildSticker.ts
|
|
2009
|
+
import { Routes as Routes13 } from "@erinjs/types";
|
|
2010
|
+
var GuildSticker = class extends Base {
|
|
2011
|
+
client;
|
|
2012
|
+
id;
|
|
2013
|
+
guildId;
|
|
2014
|
+
name;
|
|
2015
|
+
description;
|
|
2016
|
+
tags;
|
|
2017
|
+
animated;
|
|
2018
|
+
/** @param data - API sticker from GET /guilds/{id}/stickers or guild sticker events */
|
|
2019
|
+
constructor(client, data, guildId) {
|
|
2020
|
+
super();
|
|
2021
|
+
this.client = client;
|
|
2022
|
+
this.id = data.id;
|
|
2023
|
+
this.guildId = data.guild_id ?? guildId;
|
|
2024
|
+
this.name = data.name;
|
|
2025
|
+
this.description = data.description ?? "";
|
|
2026
|
+
this.tags = data.tags ?? [];
|
|
2027
|
+
this.animated = data.animated ?? false;
|
|
2028
|
+
}
|
|
2029
|
+
/** CDN URL for this sticker image. */
|
|
2030
|
+
get url() {
|
|
2031
|
+
const ext = this.animated ? "gif" : "png";
|
|
2032
|
+
return `${CDN_URL}/stickers/${this.id}.${ext}`;
|
|
2033
|
+
}
|
|
2034
|
+
/** Delete this sticker. Requires Manage Emojis and Stickers permission. */
|
|
2035
|
+
async delete() {
|
|
2036
|
+
await this.client.rest.delete(Routes13.guildSticker(this.guildId, this.id), {
|
|
2037
|
+
auth: true
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Edit this sticker's name and/or description.
|
|
2042
|
+
* Requires Manage Emojis and Stickers permission.
|
|
2043
|
+
*/
|
|
2044
|
+
async edit(options) {
|
|
2045
|
+
const data = await this.client.rest.patch(Routes13.guildSticker(this.guildId, this.id), {
|
|
2046
|
+
body: options,
|
|
2047
|
+
auth: true
|
|
2048
|
+
});
|
|
2049
|
+
const s = data;
|
|
2050
|
+
this.name = s.name;
|
|
2051
|
+
this.description = s.description ?? "";
|
|
2052
|
+
return this;
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
// src/structures/Guild.ts
|
|
2057
|
+
var Guild = class extends Base {
|
|
2058
|
+
client;
|
|
2059
|
+
id;
|
|
2060
|
+
name;
|
|
2061
|
+
icon;
|
|
2062
|
+
banner;
|
|
2063
|
+
ownerId;
|
|
2064
|
+
/** Invite splash image hash. Null if none. */
|
|
2065
|
+
splash;
|
|
2066
|
+
/** Custom vanity URL code (e.g. fluxer.gg/code). Null if none. */
|
|
2067
|
+
vanityURLCode;
|
|
2068
|
+
/** Enabled guild features. */
|
|
2069
|
+
features;
|
|
2070
|
+
verificationLevel;
|
|
2071
|
+
defaultMessageNotifications;
|
|
2072
|
+
explicitContentFilter;
|
|
2073
|
+
/** AFK voice channel ID. Null if none. */
|
|
2074
|
+
afkChannelId;
|
|
2075
|
+
/** AFK timeout in seconds. */
|
|
2076
|
+
afkTimeout;
|
|
2077
|
+
/** System messages channel ID. Null if none. */
|
|
2078
|
+
systemChannelId;
|
|
2079
|
+
/** Rules/guidelines channel ID. Null if none. */
|
|
2080
|
+
rulesChannelId;
|
|
2081
|
+
nsfwLevel;
|
|
2082
|
+
mfaLevel;
|
|
2083
|
+
/** Banner image width. Optional. */
|
|
2084
|
+
bannerWidth;
|
|
2085
|
+
/** Banner image height. Optional. */
|
|
2086
|
+
bannerHeight;
|
|
2087
|
+
/** Splash image width. Optional. */
|
|
2088
|
+
splashWidth;
|
|
2089
|
+
/** Splash image height. Optional. */
|
|
2090
|
+
splashHeight;
|
|
2091
|
+
members;
|
|
2092
|
+
channels = new Collection7();
|
|
2093
|
+
roles = new Collection7();
|
|
2094
|
+
emojis = new Collection7();
|
|
2095
|
+
stickers = new Collection7();
|
|
2096
|
+
/** @param data - API guild from GET /guilds/{id} or gateway GUILD_CREATE */
|
|
2097
|
+
constructor(client, data) {
|
|
2098
|
+
super();
|
|
2099
|
+
this.client = client;
|
|
2100
|
+
this.id = data.id;
|
|
2101
|
+
this.members = new GuildMemberManager(this);
|
|
2102
|
+
this.name = data.name;
|
|
2103
|
+
this.icon = data.icon ?? null;
|
|
2104
|
+
this.banner = data.banner ?? null;
|
|
2105
|
+
this.ownerId = data.owner_id ?? data.ownerId ?? "";
|
|
2106
|
+
this.splash = data.splash ?? null;
|
|
2107
|
+
this.vanityURLCode = data.vanity_url_code ?? null;
|
|
2108
|
+
this.features = data.features ?? [];
|
|
2109
|
+
this.verificationLevel = data.verification_level ?? 0;
|
|
2110
|
+
this.defaultMessageNotifications = data.default_message_notifications ?? 0;
|
|
2111
|
+
this.explicitContentFilter = data.explicit_content_filter ?? 0;
|
|
2112
|
+
this.afkChannelId = data.afk_channel_id ?? null;
|
|
2113
|
+
this.afkTimeout = data.afk_timeout ?? 0;
|
|
2114
|
+
this.systemChannelId = data.system_channel_id ?? null;
|
|
2115
|
+
this.rulesChannelId = data.rules_channel_id ?? null;
|
|
2116
|
+
this.nsfwLevel = data.nsfw_level ?? 0;
|
|
2117
|
+
this.mfaLevel = data.mfa_level ?? 0;
|
|
2118
|
+
this.bannerWidth = data.banner_width ?? null;
|
|
2119
|
+
this.bannerHeight = data.banner_height ?? null;
|
|
2120
|
+
this.splashWidth = data.splash_width ?? null;
|
|
2121
|
+
this.splashHeight = data.splash_height ?? null;
|
|
2122
|
+
for (const r of data.roles ?? []) {
|
|
2123
|
+
this.roles.set(r.id, new Role(client, r, this.id));
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
/** Get the guild icon URL, or null if no icon. */
|
|
2127
|
+
iconURL(options) {
|
|
2128
|
+
if (!this.icon) return null;
|
|
2129
|
+
const size = options?.size ? `?size=${options.size}` : "";
|
|
2130
|
+
return `${CDN_URL}/icons/${this.id}/${this.icon}.png${size}`;
|
|
2131
|
+
}
|
|
2132
|
+
/** Get the guild banner URL, or null if no banner. */
|
|
2133
|
+
bannerURL(options) {
|
|
2134
|
+
if (!this.banner) return null;
|
|
2135
|
+
const size = options?.size ? `?size=${options.size}` : "";
|
|
2136
|
+
return `${CDN_URL}/banners/${this.id}/${this.banner}.png${size}`;
|
|
2137
|
+
}
|
|
2138
|
+
/** Get the guild splash (invite background) URL, or null if no splash. */
|
|
2139
|
+
splashURL(options) {
|
|
2140
|
+
if (!this.splash) return null;
|
|
2141
|
+
const size = options?.size ? `?size=${options.size}` : "";
|
|
2142
|
+
return `${CDN_URL}/splashes/${this.id}/${this.splash}.png${size}`;
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Add a role to a member by user ID. Does not require fetching the member first.
|
|
2146
|
+
* @param userId - The user ID of the member
|
|
2147
|
+
* @param roleId - The role ID to add (or use guild.resolveRoleId for mention/name resolution)
|
|
2148
|
+
* Requires Manage Roles permission.
|
|
2149
|
+
*/
|
|
2150
|
+
async addRoleToMember(userId, roleId) {
|
|
2151
|
+
await this.client.rest.put(Routes14.guildMemberRole(this.id, userId, roleId));
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Remove a role from a member by user ID. Does not require fetching the member first.
|
|
2155
|
+
* @param userId - The user ID of the member
|
|
2156
|
+
* @param roleId - The role ID to remove
|
|
2157
|
+
* Requires Manage Roles permission.
|
|
2158
|
+
*/
|
|
2159
|
+
async removeRoleFromMember(userId, roleId) {
|
|
2160
|
+
await this.client.rest.delete(Routes14.guildMemberRole(this.id, userId, roleId));
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* Create a role in this guild.
|
|
2164
|
+
* Requires Manage Roles permission.
|
|
2165
|
+
* @param options - Role data (permissions accepts PermissionResolvable for convenience)
|
|
2166
|
+
* @returns The created role
|
|
2167
|
+
* @example
|
|
2168
|
+
* const role = await guild.createRole({ name: 'Mod', permissions: ['KickMembers', 'BanMembers'] });
|
|
2169
|
+
*/
|
|
2170
|
+
async createRole(options) {
|
|
2171
|
+
const body = {};
|
|
2172
|
+
if (options.name !== void 0) body.name = options.name;
|
|
2173
|
+
if (options.permissions !== void 0) {
|
|
2174
|
+
body.permissions = typeof options.permissions === "string" ? options.permissions : resolvePermissionsToBitfield2(options.permissions);
|
|
2175
|
+
}
|
|
2176
|
+
if (options.color !== void 0) body.color = options.color;
|
|
2177
|
+
if (options.hoist !== void 0) body.hoist = options.hoist;
|
|
2178
|
+
if (options.mentionable !== void 0) body.mentionable = options.mentionable;
|
|
2179
|
+
if (options.unicode_emoji !== void 0) body.unicode_emoji = options.unicode_emoji;
|
|
2180
|
+
if (options.position !== void 0) body.position = options.position;
|
|
2181
|
+
if (options.hoist_position !== void 0) body.hoist_position = options.hoist_position;
|
|
2182
|
+
const data = await this.client.rest.post(Routes14.guildRoles(this.id), {
|
|
2183
|
+
body: Object.keys(body).length ? body : void 0,
|
|
2184
|
+
auth: true
|
|
2185
|
+
});
|
|
2186
|
+
const role = new Role(this.client, data, this.id);
|
|
2187
|
+
this.roles.set(role.id, role);
|
|
2188
|
+
return role;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Fetch all roles in this guild.
|
|
2192
|
+
* @returns Array of Role objects (cached in guild.roles)
|
|
2193
|
+
*/
|
|
2194
|
+
async fetchRoles() {
|
|
2195
|
+
const data = await this.client.rest.get(
|
|
2196
|
+
Routes14.guildRoles(this.id)
|
|
2197
|
+
);
|
|
2198
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2199
|
+
const roles = [];
|
|
2200
|
+
for (const r of list) {
|
|
2201
|
+
const role = new Role(this.client, r, this.id);
|
|
2202
|
+
this.roles.set(role.id, role);
|
|
2203
|
+
roles.push(role);
|
|
2204
|
+
}
|
|
2205
|
+
return roles;
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Fetch a role by ID.
|
|
2209
|
+
* @param roleId - The role ID to fetch
|
|
2210
|
+
* @returns The role
|
|
2211
|
+
* @throws FluxerError with ROLE_NOT_FOUND if role does not exist (404)
|
|
2212
|
+
*/
|
|
2213
|
+
async fetchRole(roleId) {
|
|
2214
|
+
try {
|
|
2215
|
+
const data = await this.client.rest.get(Routes14.guildRole(this.id, roleId));
|
|
2216
|
+
const role = new Role(this.client, data, this.id);
|
|
2217
|
+
this.roles.set(role.id, role);
|
|
2218
|
+
return role;
|
|
2219
|
+
} catch (err) {
|
|
2220
|
+
const statusCode = err instanceof FluxerAPIError3 ? err.statusCode : err?.statusCode;
|
|
2221
|
+
if (statusCode === 404) {
|
|
2222
|
+
throw new FluxerError(`Role ${roleId} not found in guild`, {
|
|
2223
|
+
code: ErrorCodes.RoleNotFound,
|
|
2224
|
+
cause: err
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
throw err instanceof FluxerError ? err : new FluxerError("Failed to fetch guild role", { cause: err });
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Resolve a role ID from an argument (role mention, raw ID, or name).
|
|
2232
|
+
* Fetches guild roles if name is provided.
|
|
2233
|
+
* @param arg - Role mention (@role), role ID, or role name
|
|
2234
|
+
* @returns The role ID, or null if not found
|
|
2235
|
+
*/
|
|
2236
|
+
async resolveRoleId(arg) {
|
|
2237
|
+
const parsed = parseRoleMention(arg);
|
|
2238
|
+
if (parsed) return parsed;
|
|
2239
|
+
if (/^\d{17,19}$/.test(arg.trim())) return arg.trim();
|
|
2240
|
+
const cached = this.roles.find(
|
|
2241
|
+
(r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase())
|
|
2242
|
+
);
|
|
2243
|
+
if (cached) return cached.id;
|
|
2244
|
+
const roles = await this.client.rest.get(Routes14.guildRoles(this.id));
|
|
2245
|
+
const list = Array.isArray(roles) ? roles : Object.values(roles ?? {});
|
|
2246
|
+
const role = list.find((r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase()));
|
|
2247
|
+
if (role) {
|
|
2248
|
+
this.roles.set(role.id, new Role(this.client, role, this.id));
|
|
2249
|
+
return role.id;
|
|
2250
|
+
}
|
|
2251
|
+
return null;
|
|
2252
|
+
}
|
|
2253
|
+
/**
|
|
2254
|
+
* Ban a user from this guild.
|
|
2255
|
+
* @param userId - The user ID to ban
|
|
2256
|
+
* @param options - Optional reason, delete_message_days (0–7), and ban_duration_seconds (temporary ban).
|
|
2257
|
+
* ban_duration_seconds: 0 = permanent, or use 3600, 43200, 86400, 259200, 432000, 604800, 1209600, 2592000.
|
|
2258
|
+
* Requires Ban Members permission.
|
|
2259
|
+
*/
|
|
2260
|
+
async ban(userId, options) {
|
|
2261
|
+
const body = {};
|
|
2262
|
+
if (options?.reason) body.reason = options.reason;
|
|
2263
|
+
if (options?.delete_message_days != null)
|
|
2264
|
+
body.delete_message_days = options.delete_message_days;
|
|
2265
|
+
if (options?.ban_duration_seconds != null)
|
|
2266
|
+
body.ban_duration_seconds = options.ban_duration_seconds;
|
|
2267
|
+
await this.client.rest.put(Routes14.guildBan(this.id, userId), {
|
|
2268
|
+
body: Object.keys(body).length ? body : void 0,
|
|
2269
|
+
auth: true
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Fetch guild bans. Requires Ban Members permission.
|
|
2274
|
+
* @returns List of GuildBan objects
|
|
2275
|
+
*/
|
|
2276
|
+
async fetchBans() {
|
|
2277
|
+
const data = await this.client.rest.get(
|
|
2278
|
+
Routes14.guildBans(this.id)
|
|
2279
|
+
);
|
|
2280
|
+
const list = Array.isArray(data) ? data : data?.bans ?? [];
|
|
2281
|
+
return list.map((b) => new GuildBan(this.client, { ...b, guild_id: this.id }, this.id));
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* Remove a ban (unban a user).
|
|
2285
|
+
* @param userId - The user ID to unban
|
|
2286
|
+
* Requires Ban Members permission.
|
|
2287
|
+
*/
|
|
2288
|
+
async unban(userId) {
|
|
2289
|
+
await this.client.rest.delete(Routes14.guildBan(this.id, userId), { auth: true });
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Kick a member from this guild.
|
|
2293
|
+
* @param userId - The user ID to kick
|
|
2294
|
+
* Requires Kick Members permission.
|
|
2295
|
+
*/
|
|
2296
|
+
async kick(userId) {
|
|
2297
|
+
await this.client.rest.delete(Routes14.guildMember(this.id, userId), { auth: true });
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* Fetch a guild member by user ID.
|
|
2301
|
+
* @param userId - The user ID of the member to fetch
|
|
2302
|
+
* @returns The guild member
|
|
2303
|
+
* @throws FluxerError with MEMBER_NOT_FOUND if user is not in the guild (404)
|
|
2304
|
+
* @throws FluxerError with cause for permission denied (403) or other REST errors
|
|
2305
|
+
*/
|
|
2306
|
+
async fetchMember(userId) {
|
|
2307
|
+
try {
|
|
2308
|
+
const data = await this.client.rest.get(
|
|
2309
|
+
Routes14.guildMember(this.id, userId)
|
|
2310
|
+
);
|
|
2311
|
+
const member = new GuildMember(this.client, { ...data, guild_id: this.id }, this);
|
|
2312
|
+
this.members.set(member.id, member);
|
|
2313
|
+
return member;
|
|
2314
|
+
} catch (err) {
|
|
2315
|
+
const statusCode = err instanceof FluxerAPIError3 ? err.statusCode : err?.statusCode;
|
|
2316
|
+
if (statusCode === 404) {
|
|
2317
|
+
throw new FluxerError(`Member ${userId} not found in guild`, {
|
|
2318
|
+
code: ErrorCodes.MemberNotFound,
|
|
2319
|
+
cause: err
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
throw err instanceof FluxerError ? err : new FluxerError("Failed to fetch guild member", { cause: err });
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Fetch guild audit logs. Requires View Audit Log permission.
|
|
2327
|
+
* @param options - Optional limit, before, after, user_id, action_type for filtering
|
|
2328
|
+
*/
|
|
2329
|
+
async fetchAuditLogs(options) {
|
|
2330
|
+
const params = new URLSearchParams();
|
|
2331
|
+
if (options?.limit != null) params.set("limit", String(options.limit));
|
|
2332
|
+
if (options?.before) params.set("before", options.before);
|
|
2333
|
+
if (options?.after) params.set("after", options.after);
|
|
2334
|
+
if (options?.userId) params.set("user_id", options.userId);
|
|
2335
|
+
if (options?.actionType != null) params.set("action_type", String(options.actionType));
|
|
2336
|
+
const qs = params.toString();
|
|
2337
|
+
const url = Routes14.guildAuditLogs(this.id) + (qs ? `?${qs}` : "");
|
|
2338
|
+
return this.client.rest.get(url);
|
|
2339
|
+
}
|
|
2340
|
+
/** Fetch all webhooks in this guild. Returned webhooks do not include the token (cannot send). */
|
|
2341
|
+
async fetchWebhooks() {
|
|
2342
|
+
const data = await this.client.rest.get(Routes14.guildWebhooks(this.id));
|
|
2343
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2344
|
+
return list.map((w) => new Webhook(this.client, w));
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Fetch all invites in this guild.
|
|
2348
|
+
* Requires Manage Guild permission.
|
|
2349
|
+
*/
|
|
2350
|
+
async fetchInvites() {
|
|
2351
|
+
const data = await this.client.rest.get(
|
|
2352
|
+
Routes14.guildInvites(this.id)
|
|
2353
|
+
);
|
|
2354
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2355
|
+
return list.map((invite) => new Invite(this.client, invite));
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Fetch a guild invite by code or URL.
|
|
2359
|
+
* Convenience wrapper for Invite.fetch(client, codeOrUrl).
|
|
2360
|
+
*/
|
|
2361
|
+
async fetchInvite(codeOrUrl) {
|
|
2362
|
+
return Invite.fetch(this.client, codeOrUrl);
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Create a channel in this guild.
|
|
2366
|
+
* @param data - Channel data: type (0=text, 2=voice, 4=category, 5=link), name, and optional parent_id, topic, bitrate, user_limit, nsfw, permission_overwrites
|
|
2367
|
+
* Requires Manage Channels permission.
|
|
2368
|
+
*/
|
|
2369
|
+
async createChannel(data) {
|
|
2370
|
+
const created = await this.client.rest.post(Routes14.guildChannels(this.id), {
|
|
2371
|
+
body: data,
|
|
2372
|
+
auth: true
|
|
2373
|
+
});
|
|
2374
|
+
const channel = Channel.from(this.client, created);
|
|
2375
|
+
if (channel) {
|
|
2376
|
+
this.client.channels.set(channel.id, channel);
|
|
2377
|
+
this.channels.set(channel.id, channel);
|
|
2378
|
+
}
|
|
2379
|
+
return channel;
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* Fetch all channels in this guild.
|
|
2383
|
+
* @returns Array of GuildChannel objects (cached in guild.channels and client.channels)
|
|
2384
|
+
*/
|
|
2385
|
+
async fetchChannels() {
|
|
2386
|
+
const data = await this.client.rest.get(Routes14.guildChannels(this.id));
|
|
2387
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2388
|
+
const channels = [];
|
|
2389
|
+
for (const ch of list) {
|
|
2390
|
+
const channel = Channel.from(this.client, ch);
|
|
2391
|
+
if (channel) {
|
|
2392
|
+
this.client.channels.set(channel.id, channel);
|
|
2393
|
+
this.channels.set(channel.id, channel);
|
|
2394
|
+
channels.push(channel);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
return channels;
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Edit this guild. PATCH /guilds/{id}.
|
|
2401
|
+
* Requires guild owner or Administrator.
|
|
2402
|
+
*/
|
|
2403
|
+
async edit(options) {
|
|
2404
|
+
const data = await this.client.rest.patch(Routes14.guild(this.id), {
|
|
2405
|
+
body: options,
|
|
2406
|
+
auth: true
|
|
2407
|
+
});
|
|
2408
|
+
this.name = data.name;
|
|
2409
|
+
this.icon = data.icon ?? this.icon;
|
|
2410
|
+
this.banner = data.banner ?? this.banner;
|
|
2411
|
+
this.splash = data.splash ?? this.splash;
|
|
2412
|
+
this.systemChannelId = data.system_channel_id ?? this.systemChannelId;
|
|
2413
|
+
this.afkChannelId = data.afk_channel_id ?? this.afkChannelId;
|
|
2414
|
+
this.afkTimeout = data.afk_timeout ?? this.afkTimeout;
|
|
2415
|
+
this.verificationLevel = data.verification_level ?? this.verificationLevel;
|
|
2416
|
+
this.mfaLevel = data.mfa_level ?? this.mfaLevel;
|
|
2417
|
+
this.explicitContentFilter = data.explicit_content_filter ?? this.explicitContentFilter;
|
|
2418
|
+
this.defaultMessageNotifications = data.default_message_notifications ?? this.defaultMessageNotifications;
|
|
2419
|
+
this.features = data.features ?? this.features;
|
|
2420
|
+
return this;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Delete this guild. POST /guilds/{id}/delete.
|
|
2424
|
+
* Must be the guild owner.
|
|
2425
|
+
*/
|
|
2426
|
+
async delete() {
|
|
2427
|
+
await this.client.rest.post(Routes14.guildDelete(this.id), { auth: true });
|
|
2428
|
+
this.client.guilds.delete(this.id);
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Fetch vanity URL for this guild. GET /guilds/{id}/vanity-url.
|
|
2432
|
+
* Requires Manage Guild permission.
|
|
2433
|
+
*/
|
|
2434
|
+
async fetchVanityURL() {
|
|
2435
|
+
return this.client.rest.get(Routes14.guildVanityUrl(this.id), { auth: true });
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* Transfer guild ownership to another user. POST /guilds/{id}/transfer-ownership.
|
|
2439
|
+
* Must be the guild owner.
|
|
2440
|
+
*/
|
|
2441
|
+
async transferOwnership(newOwnerId, password) {
|
|
2442
|
+
await this.client.rest.post(Routes14.guildTransferOwnership(this.id), {
|
|
2443
|
+
body: { new_owner_id: newOwnerId, ...password != null && { password } },
|
|
2444
|
+
auth: true
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Set text channel flexible names feature. PATCH /guilds/{id}/text-channel-flexible-names.
|
|
2449
|
+
*/
|
|
2450
|
+
setTextChannelFlexibleNames(enabled) {
|
|
2451
|
+
return this.client.rest.patch(Routes14.guildTextChannelFlexibleNames(this.id), {
|
|
2452
|
+
body: { enabled },
|
|
2453
|
+
auth: true
|
|
2454
|
+
}).then(() => this);
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Set detached banner feature. PATCH /guilds/{id}/detached-banner.
|
|
2458
|
+
*/
|
|
2459
|
+
setDetachedBanner(enabled) {
|
|
2460
|
+
return this.client.rest.patch(Routes14.guildDetachedBanner(this.id), {
|
|
2461
|
+
body: { enabled },
|
|
2462
|
+
auth: true
|
|
2463
|
+
}).then(() => this);
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Set disallow unclaimed accounts. PATCH /guilds/{id}/disallow-unclaimed-accounts.
|
|
2467
|
+
*/
|
|
2468
|
+
setDisallowUnclaimedAccounts(enabled) {
|
|
2469
|
+
return this.client.rest.patch(Routes14.guildDisallowUnclaimedAccounts(this.id), {
|
|
2470
|
+
body: { enabled },
|
|
2471
|
+
auth: true
|
|
2472
|
+
}).then(() => this);
|
|
2473
|
+
}
|
|
2474
|
+
/**
|
|
2475
|
+
* Update role positions. PATCH /guilds/{id}/roles.
|
|
2476
|
+
* @param updates - Array of { id, position? }
|
|
2477
|
+
*/
|
|
2478
|
+
async setRolePositions(updates) {
|
|
2479
|
+
const data = await this.client.rest.patch(
|
|
2480
|
+
Routes14.guildRoles(this.id),
|
|
2481
|
+
{ body: updates, auth: true }
|
|
2482
|
+
);
|
|
2483
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2484
|
+
for (const r of list) {
|
|
2485
|
+
this.roles.set(r.id, new Role(this.client, r, this.id));
|
|
2486
|
+
}
|
|
2487
|
+
return list;
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* Update role hoist positions. PATCH /guilds/{id}/roles/hoist-positions.
|
|
2491
|
+
*/
|
|
2492
|
+
async setRoleHoistPositions(updates) {
|
|
2493
|
+
const data = await this.client.rest.patch(
|
|
2494
|
+
Routes14.guildRolesHoistPositions(this.id),
|
|
2495
|
+
{ body: updates, auth: true }
|
|
2496
|
+
);
|
|
2497
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2498
|
+
for (const r of list) {
|
|
2499
|
+
this.roles.set(r.id, new Role(this.client, r, this.id));
|
|
2500
|
+
}
|
|
2501
|
+
return list;
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2504
|
+
* Reset role hoist positions. DELETE /guilds/{id}/roles/hoist-positions.
|
|
2505
|
+
*/
|
|
2506
|
+
async resetRoleHoistPositions() {
|
|
2507
|
+
const data = await this.client.rest.delete(
|
|
2508
|
+
Routes14.guildRolesHoistPositions(this.id),
|
|
2509
|
+
{ auth: true }
|
|
2510
|
+
);
|
|
2511
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2512
|
+
for (const r of list) {
|
|
2513
|
+
this.roles.set(r.id, new Role(this.client, r, this.id));
|
|
2514
|
+
}
|
|
2515
|
+
return list;
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Update channel positions.
|
|
2519
|
+
* @param updates - Array of { id, position?, parent_id?, lock_permissions? }
|
|
2520
|
+
* Requires Manage Channels permission.
|
|
2521
|
+
*/
|
|
2522
|
+
async setChannelPositions(updates) {
|
|
2523
|
+
await this.client.rest.patch(Routes14.guildChannels(this.id), {
|
|
2524
|
+
body: updates,
|
|
2525
|
+
auth: true
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Fetch all emojis in this guild.
|
|
2530
|
+
* @returns Array of GuildEmoji objects (cached in guild.emojis)
|
|
2531
|
+
*/
|
|
2532
|
+
async fetchEmojis() {
|
|
2533
|
+
const data = await this.client.rest.get(
|
|
2534
|
+
Routes14.guildEmojis(this.id)
|
|
2535
|
+
);
|
|
2536
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2537
|
+
const emojis = [];
|
|
2538
|
+
for (const e of list) {
|
|
2539
|
+
const emoji = new GuildEmoji(this.client, { ...e, guild_id: this.id }, this.id);
|
|
2540
|
+
this.emojis.set(emoji.id, emoji);
|
|
2541
|
+
emojis.push(emoji);
|
|
2542
|
+
}
|
|
2543
|
+
return emojis;
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Fetch a single emoji by ID.
|
|
2547
|
+
* @param emojiId - The emoji ID to fetch
|
|
2548
|
+
* @returns The guild emoji
|
|
2549
|
+
* @throws FluxerError if emoji not found (404)
|
|
2550
|
+
*/
|
|
2551
|
+
async fetchEmoji(emojiId) {
|
|
2552
|
+
try {
|
|
2553
|
+
const data = await this.client.rest.get(Routes14.guildEmoji(this.id, emojiId));
|
|
2554
|
+
const emoji = new GuildEmoji(this.client, { ...data, guild_id: this.id }, this.id);
|
|
2555
|
+
this.emojis.set(emoji.id, emoji);
|
|
2556
|
+
return emoji;
|
|
2557
|
+
} catch (err) {
|
|
2558
|
+
const statusCode = err instanceof FluxerAPIError3 ? err.statusCode : err?.statusCode;
|
|
2559
|
+
if (statusCode === 404) {
|
|
2560
|
+
throw new FluxerError(`Emoji ${emojiId} not found in guild`, {
|
|
2561
|
+
code: ErrorCodes.EmojiNotFound,
|
|
2562
|
+
cause: err
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
throw err instanceof FluxerError ? err : new FluxerError("Failed to fetch guild emoji", { cause: err });
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* Fetch all stickers in this guild.
|
|
2570
|
+
* @returns Array of GuildSticker objects (cached in guild.stickers)
|
|
2571
|
+
*/
|
|
2572
|
+
async fetchStickers() {
|
|
2573
|
+
const data = await this.client.rest.get(
|
|
2574
|
+
Routes14.guildStickers(this.id)
|
|
2575
|
+
);
|
|
2576
|
+
const list = Array.isArray(data) ? data : Object.values(data ?? {});
|
|
2577
|
+
const stickers = [];
|
|
2578
|
+
for (const s of list) {
|
|
2579
|
+
const sticker = new GuildSticker(this.client, { ...s, guild_id: this.id }, this.id);
|
|
2580
|
+
this.stickers.set(sticker.id, sticker);
|
|
2581
|
+
stickers.push(sticker);
|
|
2582
|
+
}
|
|
2583
|
+
return stickers;
|
|
2584
|
+
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Fetch a single sticker by ID.
|
|
2587
|
+
* @param stickerId - The sticker ID to fetch
|
|
2588
|
+
*/
|
|
2589
|
+
async fetchSticker(stickerId) {
|
|
2590
|
+
const data = await this.client.rest.get(Routes14.guildSticker(this.id, stickerId));
|
|
2591
|
+
const sticker = new GuildSticker(this.client, { ...data, guild_id: this.id }, this.id);
|
|
2592
|
+
this.stickers.set(sticker.id, sticker);
|
|
2593
|
+
return sticker;
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* Bulk create emojis. POST /guilds/{id}/emojis/bulk.
|
|
2597
|
+
* @param emojis - Array of { name, image } (base64), 1-50 emojis
|
|
2598
|
+
* @returns Array of created GuildEmoji objects
|
|
2599
|
+
*/
|
|
2600
|
+
async createEmojisBulk(emojis) {
|
|
2601
|
+
const data = await this.client.rest.post(
|
|
2602
|
+
Routes14.guildEmojisBulk(this.id),
|
|
2603
|
+
{
|
|
2604
|
+
body: { emojis },
|
|
2605
|
+
auth: true
|
|
2606
|
+
}
|
|
2607
|
+
);
|
|
2608
|
+
const list = Array.isArray(data) ? data : data?.emojis ?? [];
|
|
2609
|
+
return list.map((e) => new GuildEmoji(this.client, { ...e, guild_id: this.id }, this.id));
|
|
2610
|
+
}
|
|
2611
|
+
/**
|
|
2612
|
+
* Bulk create stickers. POST /guilds/{id}/stickers/bulk.
|
|
2613
|
+
* @param stickers - Array of { name, image, description?, tags? }, 1-50 stickers
|
|
2614
|
+
* @returns Array of created GuildSticker objects
|
|
2615
|
+
*/
|
|
2616
|
+
async createStickersBulk(stickers) {
|
|
2617
|
+
const data = await this.client.rest.post(
|
|
2618
|
+
Routes14.guildStickersBulk(this.id),
|
|
2619
|
+
{
|
|
2620
|
+
body: { stickers },
|
|
2621
|
+
auth: true
|
|
2622
|
+
}
|
|
2623
|
+
);
|
|
2624
|
+
const list = Array.isArray(data) ? data : data?.stickers ?? [];
|
|
2625
|
+
const created = list.map(
|
|
2626
|
+
(s) => new GuildSticker(this.client, { ...s, guild_id: this.id }, this.id)
|
|
2627
|
+
);
|
|
2628
|
+
for (const sticker of created) {
|
|
2629
|
+
this.stickers.set(sticker.id, sticker);
|
|
2630
|
+
}
|
|
2631
|
+
return created;
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2635
|
+
// src/client/GuildManager.ts
|
|
2636
|
+
var GuildManager = class extends Collection8 {
|
|
2637
|
+
constructor(client) {
|
|
2638
|
+
super();
|
|
2639
|
+
this.client = client;
|
|
2640
|
+
this.maxSize = client.options?.cache?.guilds ?? 0;
|
|
2641
|
+
}
|
|
2642
|
+
maxSize;
|
|
2643
|
+
set(key, value) {
|
|
2644
|
+
if (this.maxSize > 0 && this.size >= this.maxSize && !this.has(key)) {
|
|
2645
|
+
const firstKey = this.keys().next().value;
|
|
2646
|
+
if (firstKey !== void 0) this.delete(firstKey);
|
|
2647
|
+
}
|
|
2648
|
+
return super.set(key, value);
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Get a guild from cache or fetch from the API if not present.
|
|
2652
|
+
* Convenience helper to avoid repeating `client.guilds.get(id) ?? (await client.guilds.fetch(id))`.
|
|
2653
|
+
* @param guildId - Snowflake of the guild
|
|
2654
|
+
* @returns The guild, or null if not found
|
|
2655
|
+
* @example
|
|
2656
|
+
* const guild = await client.guilds.resolve(message.guildId);
|
|
2657
|
+
* if (guild) console.log(guild.name);
|
|
2658
|
+
*/
|
|
2659
|
+
async resolve(guildId) {
|
|
2660
|
+
return this.get(guildId) ?? this.fetch(guildId);
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Create a guild. POST /guilds.
|
|
2664
|
+
* @param options - name (required), icon (base64), empty_features
|
|
2665
|
+
* @returns The created guild
|
|
2666
|
+
*/
|
|
2667
|
+
async create(options) {
|
|
2668
|
+
const data = await this.client.rest.post(Routes15.guilds(), {
|
|
2669
|
+
body: options,
|
|
2670
|
+
auth: true
|
|
2671
|
+
});
|
|
2672
|
+
const guild = new Guild(this.client, data);
|
|
2673
|
+
this.set(guild.id, guild);
|
|
2674
|
+
return guild;
|
|
2675
|
+
}
|
|
2676
|
+
/**
|
|
2677
|
+
* Fetch a guild by ID from the API (or return from cache if present).
|
|
2678
|
+
* @param guildId - Snowflake of the guild
|
|
2679
|
+
* @returns The guild, or null if not found
|
|
2680
|
+
* @example
|
|
2681
|
+
* const guild = await client.guilds.fetch(guildId);
|
|
2682
|
+
* if (guild) console.log(guild.name);
|
|
2683
|
+
*/
|
|
2684
|
+
async fetch(guildId) {
|
|
2685
|
+
const cached = this.get(guildId);
|
|
2686
|
+
if (cached) return cached;
|
|
2687
|
+
try {
|
|
2688
|
+
const data = await this.client.rest.get(Routes15.guild(guildId));
|
|
2689
|
+
const guild = new Guild(this.client, data);
|
|
2690
|
+
this.set(guild.id, guild);
|
|
2691
|
+
return guild;
|
|
2692
|
+
} catch {
|
|
2693
|
+
return null;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
// src/structures/User.ts
|
|
2699
|
+
import { Routes as Routes16 } from "@erinjs/types";
|
|
2700
|
+
var User = class extends Base {
|
|
2701
|
+
client;
|
|
2702
|
+
id;
|
|
2703
|
+
username;
|
|
2704
|
+
discriminator;
|
|
2705
|
+
globalName;
|
|
2706
|
+
avatar;
|
|
2707
|
+
bot;
|
|
2708
|
+
/** RGB avatar color (e.g. 7577782). Null if not set. */
|
|
2709
|
+
avatarColor;
|
|
2710
|
+
/** Public flags bitfield. Null if not set. */
|
|
2711
|
+
flags;
|
|
2712
|
+
/** Whether this is an official system user. */
|
|
2713
|
+
system;
|
|
2714
|
+
/** Banner hash (from profile, member, or invite context). Null when not available. */
|
|
2715
|
+
banner;
|
|
2716
|
+
/** @param data - API user from message author, GET /users/{id}, or GET /users/@me */
|
|
2717
|
+
constructor(client, data) {
|
|
2718
|
+
super();
|
|
2719
|
+
this.client = client;
|
|
2720
|
+
this.id = data.id;
|
|
2721
|
+
this.username = data.username;
|
|
2722
|
+
this.discriminator = data.discriminator;
|
|
2723
|
+
this.globalName = data.global_name ?? null;
|
|
2724
|
+
this.avatar = data.avatar ?? null;
|
|
2725
|
+
this.bot = !!data.bot;
|
|
2726
|
+
this.avatarColor = data.avatar_color ?? null;
|
|
2727
|
+
this.flags = data.flags ?? data.public_flags ?? null;
|
|
2728
|
+
this.system = !!data.system;
|
|
2729
|
+
this.banner = data.banner ?? null;
|
|
2730
|
+
}
|
|
2731
|
+
/** Update mutable fields from fresh API data. Used by getOrCreateUser cache. */
|
|
2732
|
+
_patch(data) {
|
|
2733
|
+
this.username = data.username;
|
|
2734
|
+
this.discriminator = data.discriminator;
|
|
2735
|
+
this.globalName = data.global_name ?? null;
|
|
2736
|
+
this.avatar = data.avatar ?? null;
|
|
2737
|
+
if (data.avatar_color !== void 0) this.avatarColor = data.avatar_color;
|
|
2738
|
+
if (data.flags !== void 0) this.flags = data.flags;
|
|
2739
|
+
if (data.banner !== void 0) this.banner = data.banner;
|
|
2740
|
+
}
|
|
2741
|
+
/**
|
|
2742
|
+
* Get the URL for this user's avatar.
|
|
2743
|
+
* Auto-detects animated avatars (hash starting with `a_`) and uses gif extension.
|
|
2744
|
+
* @param options - Optional `size` and `extension` (default: png, or gif for animated)
|
|
2745
|
+
*/
|
|
2746
|
+
avatarURL(options) {
|
|
2747
|
+
if (!this.avatar) return null;
|
|
2748
|
+
const ext = this.avatar.startsWith("a_") ? "gif" : options?.extension ?? "png";
|
|
2749
|
+
const size = options?.size ? `?size=${options.size}` : "";
|
|
2750
|
+
return `${CDN_URL}/avatars/${this.id}/${this.avatar}.${ext}${size}`;
|
|
2751
|
+
}
|
|
2752
|
+
/** Get the avatar URL, or the default avatar if none set (Fluxer: fluxerstatic.com). */
|
|
2753
|
+
displayAvatarURL(options) {
|
|
2754
|
+
return this.avatarURL(options) ?? cdnDefaultAvatarURL(this.id);
|
|
2755
|
+
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Get the URL for this user's banner.
|
|
2758
|
+
* Returns null if the user has no banner (only available when fetched from profile/member context).
|
|
2759
|
+
*/
|
|
2760
|
+
bannerURL(options) {
|
|
2761
|
+
if (!this.banner) return null;
|
|
2762
|
+
const ext = this.banner.startsWith("a_") ? "gif" : options?.extension ?? "png";
|
|
2763
|
+
const size = options?.size ? `?size=${options.size}` : "";
|
|
2764
|
+
return `${CDN_URL}/banners/${this.id}/${this.banner}.${ext}${size}`;
|
|
2765
|
+
}
|
|
2766
|
+
/** Returns a mention string (e.g. `<@123456>`). */
|
|
2767
|
+
toString() {
|
|
2768
|
+
return `<@${this.id}>`;
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Create or get a DM channel with this user.
|
|
2772
|
+
* Returns the DM channel; use {@link DMChannel.send} to send messages.
|
|
2773
|
+
*/
|
|
2774
|
+
async createDM() {
|
|
2775
|
+
const data = await this.client.rest.post(Routes16.userMeChannels(), {
|
|
2776
|
+
body: { recipient_id: this.id },
|
|
2777
|
+
auth: true
|
|
2778
|
+
});
|
|
2779
|
+
return new DMChannel(this.client, data);
|
|
2780
|
+
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Send a DM to this user.
|
|
2783
|
+
* Convenience method that creates the DM channel and sends the message.
|
|
2784
|
+
*/
|
|
2785
|
+
async send(options) {
|
|
2786
|
+
const dm = await this.createDM();
|
|
2787
|
+
return dm.send(options);
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
|
|
2791
|
+
// src/client/ClientUser.ts
|
|
2792
|
+
import { Routes as Routes17 } from "@erinjs/types";
|
|
2793
|
+
var ClientUser = class extends User {
|
|
2794
|
+
constructor(client, data) {
|
|
2795
|
+
super(client, { ...data });
|
|
2796
|
+
}
|
|
2797
|
+
/**
|
|
2798
|
+
* Fetch guilds the bot is a member of.
|
|
2799
|
+
* @returns Array of Guild objects (cached in client.guilds)
|
|
2800
|
+
*/
|
|
2801
|
+
async fetchGuilds() {
|
|
2802
|
+
const data = await this.client.rest.get(
|
|
2803
|
+
Routes17.currentUserGuilds()
|
|
2804
|
+
);
|
|
2805
|
+
const list = Array.isArray(data) ? data : data?.guilds ?? [];
|
|
2806
|
+
const guilds = [];
|
|
2807
|
+
for (const g of list) {
|
|
2808
|
+
const guild = new Guild(this.client, g);
|
|
2809
|
+
this.client.guilds.set(guild.id, guild);
|
|
2810
|
+
guilds.push(guild);
|
|
2811
|
+
}
|
|
2812
|
+
return guilds;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Leave a guild. Requires the bot to be a member.
|
|
2816
|
+
* @param guildId - The guild ID to leave
|
|
2817
|
+
*/
|
|
2818
|
+
async leaveGuild(guildId) {
|
|
2819
|
+
await this.client.rest.delete(Routes17.leaveGuild(guildId), { auth: true });
|
|
2820
|
+
this.client.guilds.delete(guildId);
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
|
|
2824
|
+
// src/client/Client.ts
|
|
2825
|
+
import {
|
|
2826
|
+
emitDeprecationWarning as emitDeprecationWarning3,
|
|
2827
|
+
formatEmoji,
|
|
2828
|
+
getUnicodeFromShortcode,
|
|
2829
|
+
parseEmoji
|
|
2830
|
+
} from "@erinjs/util";
|
|
2831
|
+
|
|
2832
|
+
// src/client/UsersManager.ts
|
|
2833
|
+
import { Collection as Collection9 } from "@erinjs/collection";
|
|
2834
|
+
import { Routes as Routes18 } from "@erinjs/types";
|
|
2835
|
+
var UsersManager = class extends Collection9 {
|
|
2836
|
+
constructor(client) {
|
|
2837
|
+
super();
|
|
2838
|
+
this.client = client;
|
|
2839
|
+
this.maxSize = client.options?.cache?.users ?? 0;
|
|
2840
|
+
}
|
|
2841
|
+
maxSize;
|
|
2842
|
+
set(key, value) {
|
|
2843
|
+
if (this.maxSize > 0 && this.size >= this.maxSize && !this.has(key)) {
|
|
2844
|
+
const firstKey = this.keys().next().value;
|
|
2845
|
+
if (firstKey !== void 0) this.delete(firstKey);
|
|
2846
|
+
}
|
|
2847
|
+
return super.set(key, value);
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Fetch a user by ID from the API.
|
|
2851
|
+
* Updates cache if user already exists.
|
|
2852
|
+
* @param userId - Snowflake of the user
|
|
2853
|
+
* @returns The user
|
|
2854
|
+
* @throws FluxerError (or REST error) if user not found
|
|
2855
|
+
* @example
|
|
2856
|
+
* const user = await client.users.fetch(userId);
|
|
2857
|
+
* console.log(user.username);
|
|
2858
|
+
*/
|
|
2859
|
+
async fetch(userId) {
|
|
2860
|
+
const data = await this.client.rest.get(Routes18.user(userId));
|
|
2861
|
+
return this.client.getOrCreateUser(data);
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* Fetch a user with full profile and optional guild context.
|
|
2865
|
+
* Returns user, global profile, server profile (when guildId), and member (when guildId).
|
|
2866
|
+
* Ideal for userinfo commands.
|
|
2867
|
+
* @param userId - Snowflake of the user
|
|
2868
|
+
* @param options - Optional guildId for server profile and member data
|
|
2869
|
+
* @returns User, raw data, profiles, and member (when in guild)
|
|
2870
|
+
* @throws FluxerError (or REST error) if user not found
|
|
2871
|
+
* @example
|
|
2872
|
+
* const { user, globalProfile, serverProfile, member } = await client.users.fetchWithProfile(
|
|
2873
|
+
* userId,
|
|
2874
|
+
* { guildId: message.guildId ?? undefined },
|
|
2875
|
+
* );
|
|
2876
|
+
*/
|
|
2877
|
+
async fetchWithProfile(userId, options) {
|
|
2878
|
+
const guildId = options?.guildId ?? void 0;
|
|
2879
|
+
const [userData, globalProfileData, serverProfileData, memberData] = await Promise.all([
|
|
2880
|
+
this.client.rest.get(Routes18.user(userId)),
|
|
2881
|
+
this.client.rest.get(Routes18.userProfile(userId)).catch(() => null),
|
|
2882
|
+
guildId ? this.client.rest.get(Routes18.userProfile(userId, guildId)).catch(() => null) : Promise.resolve(null),
|
|
2883
|
+
guildId ? this.client.rest.get(Routes18.guildMember(guildId, userId)).catch(() => null) : Promise.resolve(null)
|
|
2884
|
+
]);
|
|
2885
|
+
const user = this.client.getOrCreateUser(userData);
|
|
2886
|
+
const globalProfile = globalProfileData && typeof globalProfileData === "object" ? globalProfileData : null;
|
|
2887
|
+
const serverProfile = serverProfileData && typeof serverProfileData === "object" ? serverProfileData : null;
|
|
2888
|
+
let member = null;
|
|
2889
|
+
if (memberData && guildId) {
|
|
2890
|
+
const guild = this.client.guilds.get(guildId) ?? await this.client.guilds.fetch(guildId);
|
|
2891
|
+
if (guild) {
|
|
2892
|
+
member = new GuildMember(this.client, { ...memberData, guild_id: guildId }, guild);
|
|
2893
|
+
guild.members.set(member.id, member);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
return {
|
|
2897
|
+
user,
|
|
2898
|
+
userData,
|
|
2899
|
+
globalProfile,
|
|
2900
|
+
serverProfile,
|
|
2901
|
+
member,
|
|
2902
|
+
memberData
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
};
|
|
2906
|
+
|
|
2907
|
+
// src/util/guildUtils.ts
|
|
2908
|
+
function normalizeGuildPayload(raw) {
|
|
2909
|
+
if (!raw || typeof raw !== "object") {
|
|
2910
|
+
return null;
|
|
2911
|
+
}
|
|
2912
|
+
if ("properties" in raw && raw.properties != null && typeof raw.properties === "object") {
|
|
2913
|
+
const r = raw;
|
|
2914
|
+
return {
|
|
2915
|
+
...r.properties,
|
|
2916
|
+
roles: r.roles
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
return raw;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
// src/structures/MessageReaction.ts
|
|
2923
|
+
import { Routes as Routes19 } from "@erinjs/types";
|
|
2924
|
+
import { FluxerAPIError as FluxerAPIError4, RateLimitError as RateLimitError3 } from "@erinjs/rest";
|
|
2925
|
+
var MessageReaction = class extends Base {
|
|
2926
|
+
client;
|
|
2927
|
+
messageId;
|
|
2928
|
+
channelId;
|
|
2929
|
+
guildId;
|
|
2930
|
+
emoji;
|
|
2931
|
+
/** Raw gateway payload for low-level access. */
|
|
2932
|
+
_data;
|
|
2933
|
+
constructor(client, data) {
|
|
2934
|
+
super();
|
|
2935
|
+
this.client = client;
|
|
2936
|
+
this._data = data;
|
|
2937
|
+
this.messageId = data.message_id;
|
|
2938
|
+
this.channelId = data.channel_id;
|
|
2939
|
+
this.guildId = data.guild_id ?? null;
|
|
2940
|
+
this.emoji = data.emoji;
|
|
2941
|
+
}
|
|
2942
|
+
/** Emoji as a string: unicode or "name:id" for custom. */
|
|
2943
|
+
get emojiIdentifier() {
|
|
2944
|
+
return this.emoji.id ? `${this.emoji.name}:${this.emoji.id}` : this.emoji.name;
|
|
2945
|
+
}
|
|
2946
|
+
/** Guild where this reaction was added. Resolved from cache; null for DMs or if not cached. */
|
|
2947
|
+
get guild() {
|
|
2948
|
+
return this.guildId ? this.client.guilds.get(this.guildId) ?? null : null;
|
|
2949
|
+
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Fetch the message this reaction belongs to.
|
|
2952
|
+
* Use when you need to edit, delete, or otherwise interact with the message.
|
|
2953
|
+
* @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
|
|
2954
|
+
*/
|
|
2955
|
+
async fetchMessage() {
|
|
2956
|
+
try {
|
|
2957
|
+
const data = await this.client.rest.get(
|
|
2958
|
+
Routes19.channelMessage(this.channelId, this.messageId)
|
|
2959
|
+
);
|
|
2960
|
+
return new Message(this.client, data);
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
if (err instanceof RateLimitError3) throw err;
|
|
2963
|
+
if (err instanceof FluxerAPIError4 && err.statusCode === 404) {
|
|
2964
|
+
throw new FluxerError(`Message ${this.messageId} not found in channel ${this.channelId}`, {
|
|
2965
|
+
code: ErrorCodes.MessageNotFound,
|
|
2966
|
+
cause: err
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
};
|
|
2973
|
+
|
|
2974
|
+
// src/client/EventHandlerRegistry.ts
|
|
2975
|
+
var handlers = /* @__PURE__ */ new Map();
|
|
2976
|
+
function normalizeInviteCreatePayload(client, payload) {
|
|
2977
|
+
if (!payload?.code) return null;
|
|
2978
|
+
const guildId = payload.guild?.id ?? payload.guild_id ?? "0";
|
|
2979
|
+
const cachedGuild = guildId !== "0" ? client.guilds.get(guildId) : null;
|
|
2980
|
+
const guild = {
|
|
2981
|
+
id: guildId,
|
|
2982
|
+
name: payload.guild?.name ?? cachedGuild?.name ?? "Unknown Guild",
|
|
2983
|
+
icon: payload.guild?.icon ?? null,
|
|
2984
|
+
banner: payload.guild?.banner ?? null,
|
|
2985
|
+
splash: payload.guild?.splash ?? null,
|
|
2986
|
+
features: payload.guild?.features
|
|
2987
|
+
};
|
|
2988
|
+
const channelId = payload.channel?.id ?? payload.channel_id ?? "0";
|
|
2989
|
+
const cachedChannel = channelId !== "0" ? client.channels.get(channelId) : null;
|
|
2990
|
+
const channel = {
|
|
2991
|
+
id: channelId,
|
|
2992
|
+
type: payload.channel?.type ?? cachedChannel?.type ?? 0,
|
|
2993
|
+
name: payload.channel?.name ?? cachedChannel?.name ?? null,
|
|
2994
|
+
icon: payload.channel?.icon ?? null
|
|
2995
|
+
};
|
|
2996
|
+
return {
|
|
2997
|
+
code: payload.code,
|
|
2998
|
+
type: typeof payload.type === "number" ? payload.type : 0,
|
|
2999
|
+
guild,
|
|
3000
|
+
channel,
|
|
3001
|
+
inviter: payload.inviter ?? null,
|
|
3002
|
+
member_count: payload.member_count,
|
|
3003
|
+
presence_count: payload.presence_count,
|
|
3004
|
+
expires_at: payload.expires_at,
|
|
3005
|
+
temporary: payload.temporary,
|
|
3006
|
+
created_at: payload.created_at,
|
|
3007
|
+
uses: payload.uses,
|
|
3008
|
+
max_uses: payload.max_uses,
|
|
3009
|
+
max_age: payload.max_age
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
handlers.set("MESSAGE_CREATE", async (client, d) => {
|
|
3013
|
+
const data = d;
|
|
3014
|
+
if (data.guild_id && data.member && data.author) {
|
|
3015
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3016
|
+
if (guild) {
|
|
3017
|
+
const memberData = { ...data.member, user: data.author, guild_id: data.guild_id };
|
|
3018
|
+
const member = new GuildMember(client, memberData, guild);
|
|
3019
|
+
guild.members.set(member.id, member);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
client._addMessageToCache(data.channel_id, data);
|
|
3023
|
+
client.emit(Events.MessageCreate, new Message(client, data));
|
|
3024
|
+
});
|
|
3025
|
+
handlers.set("MESSAGE_UPDATE", async (client, d) => {
|
|
3026
|
+
const partial = d;
|
|
3027
|
+
const cache = client._getMessageCache(partial.channel_id);
|
|
3028
|
+
let oldMessage = null;
|
|
3029
|
+
let mergedData = partial;
|
|
3030
|
+
if (cache) {
|
|
3031
|
+
const oldData = cache.get(partial.id);
|
|
3032
|
+
if (oldData) {
|
|
3033
|
+
oldMessage = new Message(client, oldData);
|
|
3034
|
+
mergedData = { ...oldData, ...partial };
|
|
3035
|
+
}
|
|
3036
|
+
cache.set(partial.id, mergedData);
|
|
3037
|
+
}
|
|
3038
|
+
const newMessage = new Message(client, mergedData);
|
|
3039
|
+
client.emit(Events.MessageUpdate, oldMessage, newMessage);
|
|
3040
|
+
});
|
|
3041
|
+
handlers.set("MESSAGE_DELETE", async (client, d) => {
|
|
3042
|
+
const data = d;
|
|
3043
|
+
client._removeMessageFromCache(data.channel_id, data.id);
|
|
3044
|
+
const channel = client.channels.get(data.channel_id) ?? null;
|
|
3045
|
+
client.emit(Events.MessageDelete, {
|
|
3046
|
+
id: data.id,
|
|
3047
|
+
channelId: data.channel_id,
|
|
3048
|
+
channel,
|
|
3049
|
+
content: data.content ?? null,
|
|
3050
|
+
authorId: data.author_id ?? null
|
|
3051
|
+
});
|
|
3052
|
+
});
|
|
3053
|
+
handlers.set("MESSAGE_REACTION_ADD", async (client, d) => {
|
|
3054
|
+
const data = d;
|
|
3055
|
+
const reaction = new MessageReaction(client, data);
|
|
3056
|
+
const user = client.getOrCreateUser({
|
|
3057
|
+
id: data.user_id,
|
|
3058
|
+
username: "Unknown",
|
|
3059
|
+
discriminator: "0"
|
|
3060
|
+
});
|
|
3061
|
+
client.emit(Events.MessageReactionAdd, reaction, user);
|
|
3062
|
+
});
|
|
3063
|
+
handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
|
|
3064
|
+
const data = d;
|
|
3065
|
+
const reaction = new MessageReaction(client, data);
|
|
3066
|
+
const user = client.getOrCreateUser({
|
|
3067
|
+
id: data.user_id,
|
|
3068
|
+
username: "Unknown",
|
|
3069
|
+
discriminator: "0"
|
|
3070
|
+
});
|
|
3071
|
+
client.emit(Events.MessageReactionRemove, reaction, user);
|
|
3072
|
+
});
|
|
3073
|
+
handlers.set("MESSAGE_REACTION_REMOVE_ALL", async (client, d) => {
|
|
3074
|
+
client.emit(Events.MessageReactionRemoveAll, d);
|
|
3075
|
+
});
|
|
3076
|
+
handlers.set("MESSAGE_REACTION_REMOVE_EMOJI", async (client, d) => {
|
|
3077
|
+
client.emit(
|
|
3078
|
+
Events.MessageReactionRemoveEmoji,
|
|
3079
|
+
d
|
|
3080
|
+
);
|
|
3081
|
+
});
|
|
3082
|
+
handlers.set("GUILD_CREATE", async (client, d) => {
|
|
3083
|
+
const guildData = normalizeGuildPayload(d);
|
|
3084
|
+
if (!guildData) return;
|
|
3085
|
+
const guild = new Guild(client, guildData);
|
|
3086
|
+
client.guilds.set(guild.id, guild);
|
|
3087
|
+
const g = d;
|
|
3088
|
+
for (const ch of g.channels ?? []) {
|
|
3089
|
+
const channel = Channel.from(client, ch);
|
|
3090
|
+
if (channel) {
|
|
3091
|
+
client.channels.set(channel.id, channel);
|
|
3092
|
+
guild.channels.set(channel.id, channel);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
for (const m of g.members ?? []) {
|
|
3096
|
+
if (m?.user?.id) {
|
|
3097
|
+
const memberData = { ...m, guild_id: guild.id };
|
|
3098
|
+
const member = new GuildMember(client, memberData, guild);
|
|
3099
|
+
guild.members.set(member.id, member);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
client.emit(Events.GuildCreate, guild);
|
|
3103
|
+
if (g.voice_states?.length) {
|
|
3104
|
+
client.emit(Events.VoiceStatesSync, { guildId: guild.id, voiceStates: g.voice_states });
|
|
3105
|
+
}
|
|
3106
|
+
client._onGuildReceived(guild.id);
|
|
3107
|
+
});
|
|
3108
|
+
handlers.set("GUILD_UPDATE", async (client, d) => {
|
|
3109
|
+
const guildData = normalizeGuildPayload(d);
|
|
3110
|
+
if (!guildData) return;
|
|
3111
|
+
const old = client.guilds.get(guildData.id);
|
|
3112
|
+
const updated = new Guild(client, guildData);
|
|
3113
|
+
client.guilds.set(updated.id, updated);
|
|
3114
|
+
client.emit(Events.GuildUpdate, old ?? updated, updated);
|
|
3115
|
+
});
|
|
3116
|
+
handlers.set("GUILD_DELETE", async (client, d) => {
|
|
3117
|
+
const g = d;
|
|
3118
|
+
const guild = client.guilds.get(g.id);
|
|
3119
|
+
if (guild) {
|
|
3120
|
+
client.guilds.delete(g.id);
|
|
3121
|
+
client.emit(Events.GuildDelete, guild);
|
|
3122
|
+
}
|
|
3123
|
+
});
|
|
3124
|
+
handlers.set("CHANNEL_CREATE", async (client, d) => {
|
|
3125
|
+
const ch = Channel.from(client, d);
|
|
3126
|
+
if (ch) {
|
|
3127
|
+
client.channels.set(ch.id, ch);
|
|
3128
|
+
if ("guildId" in ch && ch.guildId) {
|
|
3129
|
+
const guild = client.guilds.get(ch.guildId);
|
|
3130
|
+
if (guild) guild.channels.set(ch.id, ch);
|
|
3131
|
+
}
|
|
3132
|
+
client.emit(Events.ChannelCreate, ch);
|
|
3133
|
+
}
|
|
3134
|
+
});
|
|
3135
|
+
handlers.set("CHANNEL_UPDATE", async (client, d) => {
|
|
3136
|
+
const ch = d;
|
|
3137
|
+
const oldCh = client.channels.get(ch.id);
|
|
3138
|
+
const newCh = Channel.from(client, ch);
|
|
3139
|
+
if (newCh) {
|
|
3140
|
+
client.channels.set(newCh.id, newCh);
|
|
3141
|
+
if ("guildId" in newCh && newCh.guildId) {
|
|
3142
|
+
const guild = client.guilds.get(newCh.guildId);
|
|
3143
|
+
if (guild) guild.channels.set(newCh.id, newCh);
|
|
3144
|
+
}
|
|
3145
|
+
client.emit(Events.ChannelUpdate, oldCh ?? newCh, newCh);
|
|
3146
|
+
}
|
|
3147
|
+
});
|
|
3148
|
+
handlers.set("CHANNEL_DELETE", async (client, d) => {
|
|
3149
|
+
const ch = d;
|
|
3150
|
+
const channel = client.channels.get(ch.id);
|
|
3151
|
+
if (channel) {
|
|
3152
|
+
if ("guildId" in channel && channel.guildId) {
|
|
3153
|
+
const guild = client.guilds.get(channel.guildId);
|
|
3154
|
+
if (guild) guild.channels.delete(channel.id);
|
|
3155
|
+
}
|
|
3156
|
+
client.channels.delete(ch.id);
|
|
3157
|
+
client.emit(Events.ChannelDelete, channel);
|
|
3158
|
+
}
|
|
3159
|
+
});
|
|
3160
|
+
handlers.set("GUILD_MEMBER_ADD", async (client, d) => {
|
|
3161
|
+
const data = d;
|
|
3162
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3163
|
+
if (guild) {
|
|
3164
|
+
const member = new GuildMember(client, data, guild);
|
|
3165
|
+
guild.members.set(member.id, member);
|
|
3166
|
+
client.emit(Events.GuildMemberAdd, member);
|
|
3167
|
+
}
|
|
3168
|
+
});
|
|
3169
|
+
handlers.set("GUILD_MEMBER_UPDATE", async (client, d) => {
|
|
3170
|
+
const data = d;
|
|
3171
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3172
|
+
if (guild) {
|
|
3173
|
+
const oldM = guild.members.get(data.user.id);
|
|
3174
|
+
const newM = new GuildMember(client, data, guild);
|
|
3175
|
+
guild.members.set(newM.id, newM);
|
|
3176
|
+
client.emit(Events.GuildMemberUpdate, oldM ?? newM, newM);
|
|
3177
|
+
}
|
|
3178
|
+
});
|
|
3179
|
+
handlers.set("GUILD_MEMBER_REMOVE", async (client, d) => {
|
|
3180
|
+
const data = d;
|
|
3181
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3182
|
+
if (!guild || !data.user?.id) return;
|
|
3183
|
+
let member = guild.members.get(data.user.id);
|
|
3184
|
+
if (member) {
|
|
3185
|
+
guild.members.delete(data.user.id);
|
|
3186
|
+
} else {
|
|
3187
|
+
const user = {
|
|
3188
|
+
...data.user,
|
|
3189
|
+
id: data.user.id,
|
|
3190
|
+
username: data.user.username ?? "Unknown",
|
|
3191
|
+
discriminator: data.user.discriminator ?? "0"
|
|
3192
|
+
};
|
|
3193
|
+
const memberData = {
|
|
3194
|
+
user,
|
|
3195
|
+
roles: [],
|
|
3196
|
+
joined_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3197
|
+
nick: null
|
|
3198
|
+
};
|
|
3199
|
+
member = new GuildMember(client, memberData, guild);
|
|
3200
|
+
}
|
|
3201
|
+
client.emit(Events.GuildMemberRemove, member);
|
|
3202
|
+
});
|
|
3203
|
+
handlers.set("GUILD_MEMBERS_CHUNK", async (client, d) => {
|
|
3204
|
+
const data = d;
|
|
3205
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3206
|
+
if (guild) {
|
|
3207
|
+
for (const m of data.members ?? []) {
|
|
3208
|
+
if (m?.user?.id) {
|
|
3209
|
+
const memberData = { ...m, guild_id: guild.id };
|
|
3210
|
+
const member = new GuildMember(client, memberData, guild);
|
|
3211
|
+
guild.members.set(member.id, member);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
client.emit(Events.GuildMembersChunk, data);
|
|
3216
|
+
});
|
|
3217
|
+
handlers.set("INTERACTION_CREATE", async (client, d) => {
|
|
3218
|
+
client.emit(Events.InteractionCreate, d);
|
|
3219
|
+
});
|
|
3220
|
+
handlers.set("VOICE_STATE_UPDATE", async (client, d) => {
|
|
3221
|
+
client.emit(Events.VoiceStateUpdate, d);
|
|
3222
|
+
});
|
|
3223
|
+
handlers.set("VOICE_SERVER_UPDATE", async (client, d) => {
|
|
3224
|
+
client.emit(Events.VoiceServerUpdate, d);
|
|
3225
|
+
});
|
|
3226
|
+
handlers.set("MESSAGE_DELETE_BULK", async (client, d) => {
|
|
3227
|
+
const data = d;
|
|
3228
|
+
for (const id of data.ids ?? []) {
|
|
3229
|
+
client._removeMessageFromCache(data.channel_id, id);
|
|
3230
|
+
}
|
|
3231
|
+
client.emit(Events.MessageDeleteBulk, data);
|
|
3232
|
+
});
|
|
3233
|
+
handlers.set("GUILD_BAN_ADD", async (client, d) => {
|
|
3234
|
+
const data = d;
|
|
3235
|
+
const banData = {
|
|
3236
|
+
user: data.user,
|
|
3237
|
+
reason: data.reason ?? null,
|
|
3238
|
+
guild_id: data.guild_id
|
|
3239
|
+
};
|
|
3240
|
+
const ban = new GuildBan(client, banData, data.guild_id);
|
|
3241
|
+
client.emit(Events.GuildBanAdd, ban);
|
|
3242
|
+
});
|
|
3243
|
+
handlers.set("GUILD_BAN_REMOVE", async (client, d) => {
|
|
3244
|
+
const data = d;
|
|
3245
|
+
const ban = new GuildBan(client, { ...data, reason: null }, data.guild_id);
|
|
3246
|
+
client.emit(Events.GuildBanRemove, ban);
|
|
3247
|
+
});
|
|
3248
|
+
handlers.set("GUILD_EMOJIS_UPDATE", async (client, d) => {
|
|
3249
|
+
const data = d;
|
|
3250
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3251
|
+
if (guild) {
|
|
3252
|
+
guild.emojis.clear();
|
|
3253
|
+
for (const e of data.emojis ?? []) {
|
|
3254
|
+
if (!e.id || e.name == null) continue;
|
|
3255
|
+
guild.emojis.set(
|
|
3256
|
+
e.id,
|
|
3257
|
+
new GuildEmoji(
|
|
3258
|
+
client,
|
|
3259
|
+
{ id: e.id, name: e.name, animated: e.animated ?? false, guild_id: guild.id },
|
|
3260
|
+
guild.id
|
|
3261
|
+
)
|
|
3262
|
+
);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
client.emit(Events.GuildEmojisUpdate, d);
|
|
3266
|
+
});
|
|
3267
|
+
handlers.set("GUILD_STICKERS_UPDATE", async (client, d) => {
|
|
3268
|
+
client.emit(Events.GuildStickersUpdate, d);
|
|
3269
|
+
});
|
|
3270
|
+
handlers.set("GUILD_INTEGRATIONS_UPDATE", async (client, d) => {
|
|
3271
|
+
client.emit(Events.GuildIntegrationsUpdate, d);
|
|
3272
|
+
});
|
|
3273
|
+
handlers.set("GUILD_ROLE_CREATE", async (client, d) => {
|
|
3274
|
+
const data = d;
|
|
3275
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3276
|
+
if (guild) {
|
|
3277
|
+
guild.roles.set(data.role.id, new Role(client, data.role, guild.id));
|
|
3278
|
+
}
|
|
3279
|
+
client.emit(Events.GuildRoleCreate, data);
|
|
3280
|
+
});
|
|
3281
|
+
handlers.set("GUILD_ROLE_UPDATE", async (client, d) => {
|
|
3282
|
+
const data = d;
|
|
3283
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3284
|
+
if (guild) {
|
|
3285
|
+
const existing = guild.roles.get(data.role.id);
|
|
3286
|
+
if (existing) {
|
|
3287
|
+
existing._patch(data.role);
|
|
3288
|
+
} else {
|
|
3289
|
+
guild.roles.set(data.role.id, new Role(client, data.role, guild.id));
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
client.emit(Events.GuildRoleUpdate, data);
|
|
3293
|
+
});
|
|
3294
|
+
handlers.set("GUILD_ROLE_DELETE", async (client, d) => {
|
|
3295
|
+
const data = d;
|
|
3296
|
+
const guild = client.guilds.get(data.guild_id);
|
|
3297
|
+
if (guild) guild.roles.delete(data.role_id);
|
|
3298
|
+
client.emit(Events.GuildRoleDelete, data);
|
|
3299
|
+
});
|
|
3300
|
+
handlers.set("GUILD_SCHEDULED_EVENT_CREATE", async (client, d) => {
|
|
3301
|
+
client.emit(Events.GuildScheduledEventCreate, d);
|
|
3302
|
+
});
|
|
3303
|
+
handlers.set("GUILD_SCHEDULED_EVENT_UPDATE", async (client, d) => {
|
|
3304
|
+
client.emit(Events.GuildScheduledEventUpdate, d);
|
|
3305
|
+
});
|
|
3306
|
+
handlers.set("GUILD_SCHEDULED_EVENT_DELETE", async (client, d) => {
|
|
3307
|
+
client.emit(Events.GuildScheduledEventDelete, d);
|
|
3308
|
+
});
|
|
3309
|
+
handlers.set("CHANNEL_PINS_UPDATE", async (client, d) => {
|
|
3310
|
+
client.emit(Events.ChannelPinsUpdate, d);
|
|
3311
|
+
});
|
|
3312
|
+
handlers.set("INVITE_CREATE", async (client, d) => {
|
|
3313
|
+
const data = normalizeInviteCreatePayload(client, d);
|
|
3314
|
+
if (!data) {
|
|
3315
|
+
client.emit(
|
|
3316
|
+
Events.Debug,
|
|
3317
|
+
"[Gateway] INVITE_CREATE payload had no invite code (documented as possibly empty)"
|
|
3318
|
+
);
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
client.emit(
|
|
3322
|
+
Events.Debug,
|
|
3323
|
+
`[Gateway] INVITE_CREATE code=${data.code} guild=${data.guild.id} channel=${data.channel.id}`
|
|
3324
|
+
);
|
|
3325
|
+
client.emit(Events.InviteCreate, new Invite(client, data));
|
|
3326
|
+
});
|
|
3327
|
+
handlers.set("INVITE_DELETE", async (client, d) => {
|
|
3328
|
+
client.emit(Events.InviteDelete, d);
|
|
3329
|
+
});
|
|
3330
|
+
handlers.set("TYPING_START", async (client, d) => {
|
|
3331
|
+
client.emit(Events.TypingStart, d);
|
|
3332
|
+
});
|
|
3333
|
+
handlers.set("USER_UPDATE", async (client, d) => {
|
|
3334
|
+
const data = d;
|
|
3335
|
+
if (client.user?.id === data.id) {
|
|
3336
|
+
client.user._patch(data);
|
|
3337
|
+
}
|
|
3338
|
+
client.emit(Events.UserUpdate, data);
|
|
3339
|
+
});
|
|
3340
|
+
handlers.set("PRESENCE_UPDATE", async (client, d) => {
|
|
3341
|
+
client.emit(Events.PresenceUpdate, d);
|
|
3342
|
+
});
|
|
3343
|
+
handlers.set("WEBHOOKS_UPDATE", async (client, d) => {
|
|
3344
|
+
client.emit(Events.WebhooksUpdate, d);
|
|
3345
|
+
});
|
|
3346
|
+
handlers.set("RESUMED", async (client) => {
|
|
3347
|
+
client.emit(Events.Resumed);
|
|
3348
|
+
});
|
|
3349
|
+
var eventHandlers = handlers;
|
|
3350
|
+
|
|
3351
|
+
// src/client/Client.ts
|
|
3352
|
+
function createEventMethods(client) {
|
|
3353
|
+
const result = {};
|
|
3354
|
+
for (const key of Object.keys(Events)) {
|
|
3355
|
+
const eventName = Events[key];
|
|
3356
|
+
result[key] = (cb) => {
|
|
3357
|
+
client.on(eventName, cb);
|
|
3358
|
+
return client;
|
|
3359
|
+
};
|
|
3360
|
+
}
|
|
3361
|
+
return result;
|
|
3362
|
+
}
|
|
3363
|
+
var Client = class extends EventEmitter3 {
|
|
3364
|
+
/** @param options - Token, REST config, WebSocket, presence, etc. */
|
|
3365
|
+
constructor(options = {}) {
|
|
3366
|
+
super();
|
|
3367
|
+
this.options = options;
|
|
3368
|
+
this.events = createEventMethods(this);
|
|
3369
|
+
Object.defineProperty(this.channels, "cache", {
|
|
3370
|
+
get: () => this.channels,
|
|
3371
|
+
configurable: true
|
|
3372
|
+
});
|
|
3373
|
+
Object.defineProperty(this.guilds, "cache", {
|
|
3374
|
+
get: () => this.guilds,
|
|
3375
|
+
configurable: true
|
|
3376
|
+
});
|
|
3377
|
+
Object.defineProperty(this.users, "cache", {
|
|
3378
|
+
get: () => this.users,
|
|
3379
|
+
configurable: true
|
|
3380
|
+
});
|
|
3381
|
+
this.rest = new REST({
|
|
3382
|
+
api: options.rest?.api ?? "https://api.fluxer.app",
|
|
3383
|
+
version: options.rest?.version ?? "1",
|
|
3384
|
+
...options.rest
|
|
3385
|
+
});
|
|
3386
|
+
}
|
|
3387
|
+
rest;
|
|
3388
|
+
guilds = new GuildManager(this);
|
|
3389
|
+
channels = new ChannelManager(this);
|
|
3390
|
+
users = new UsersManager(this);
|
|
3391
|
+
/** Typed event handlers. Use client.events.MessageReactionAdd((reaction, user, messageId, channelId, emoji, userId) => {...}) or client.on(Events.MessageReactionAdd, ...). */
|
|
3392
|
+
events;
|
|
3393
|
+
/** The authenticated bot user. Null until READY is received. */
|
|
3394
|
+
user = null;
|
|
3395
|
+
/** Timestamp when the client became ready. Null until READY is received. */
|
|
3396
|
+
readyAt = null;
|
|
3397
|
+
_ws = null;
|
|
3398
|
+
/** When waitForGuilds, set of guild IDs we're waiting for GUILD_CREATE on. Null when not waiting. */
|
|
3399
|
+
_pendingGuildIds = null;
|
|
3400
|
+
/** Timeout for guild stream when READY has no guilds (gateway sends only GUILD_CREATE). Cleared in finally. */
|
|
3401
|
+
_guildStreamSettleTimeout = null;
|
|
3402
|
+
/** Per-channel message cache (channelId -> messageId -> APIMessage). Used when options.cache.messages > 0. */
|
|
3403
|
+
_messageCaches = null;
|
|
3404
|
+
on(event, listener) {
|
|
3405
|
+
return super.on(event, listener);
|
|
3406
|
+
}
|
|
3407
|
+
once(event, listener) {
|
|
3408
|
+
return super.once(event, listener);
|
|
3409
|
+
}
|
|
3410
|
+
off(event, listener) {
|
|
3411
|
+
return super.off(event, listener);
|
|
3412
|
+
}
|
|
3413
|
+
emit(event, ...args) {
|
|
3414
|
+
return super.emit(event, ...args);
|
|
3415
|
+
}
|
|
3416
|
+
/**
|
|
3417
|
+
* Resolve an emoji argument to the API format (unicode or "name:id").
|
|
3418
|
+
* Supports: <:name:id>, :name:, name:id, { name, id }, unicode.
|
|
3419
|
+
* When id is missing (e.g. :name:), fetches guild emojis if guildId provided.
|
|
3420
|
+
* When reacting in a guild channel, custom emojis must be from that guild.
|
|
3421
|
+
* @param emoji - Emoji string or object
|
|
3422
|
+
* @param guildId - Guild ID for resolving custom emoji by name (required when id is missing)
|
|
3423
|
+
* @returns API-formatted string for reactions
|
|
3424
|
+
*/
|
|
3425
|
+
async resolveEmoji(emoji, guildId) {
|
|
3426
|
+
if (typeof emoji === "object" && emoji.id) {
|
|
3427
|
+
if (guildId) {
|
|
3428
|
+
await this.assertEmojiInGuild(emoji.id, guildId);
|
|
3429
|
+
}
|
|
3430
|
+
return formatEmoji({ name: emoji.name, id: emoji.id, animated: emoji.animated });
|
|
3431
|
+
}
|
|
3432
|
+
const parsed = parseEmoji(
|
|
3433
|
+
typeof emoji === "string" ? emoji : emoji.id ? `:${emoji.name}:` : emoji.name
|
|
3434
|
+
);
|
|
3435
|
+
if (!parsed) throw new Error("Invalid emoji");
|
|
3436
|
+
if (parsed.id) {
|
|
3437
|
+
if (guildId) {
|
|
3438
|
+
await this.assertEmojiInGuild(parsed.id, guildId);
|
|
3439
|
+
}
|
|
3440
|
+
return formatEmoji(parsed);
|
|
3441
|
+
}
|
|
3442
|
+
if (!/^\w+$/.test(parsed.name)) return parsed.name;
|
|
3443
|
+
const unicodeFromShortcode = getUnicodeFromShortcode(parsed.name);
|
|
3444
|
+
if (unicodeFromShortcode) return unicodeFromShortcode;
|
|
3445
|
+
if (guildId) {
|
|
3446
|
+
const emojis = await this.rest.get(Routes20.guildEmojis(guildId));
|
|
3447
|
+
const list = Array.isArray(emojis) ? emojis : Object.values(emojis ?? {});
|
|
3448
|
+
const found = list.find((e) => e.name && e.name.toLowerCase() === parsed.name.toLowerCase());
|
|
3449
|
+
if (found) return formatEmoji({ ...parsed, id: found.id, animated: found.animated });
|
|
3450
|
+
throw new Error(
|
|
3451
|
+
`Custom emoji ":${parsed.name}:" not found in guild. Use name:id or <:name:id> format.`
|
|
3452
|
+
);
|
|
3453
|
+
}
|
|
3454
|
+
if (/^\w+$/.test(parsed.name)) {
|
|
3455
|
+
throw new Error(
|
|
3456
|
+
`Custom emoji ":${parsed.name}:" requires guild context. Use message.react() in a guild channel, or pass guildId to client.resolveEmoji().`
|
|
3457
|
+
);
|
|
3458
|
+
}
|
|
3459
|
+
return parsed.name;
|
|
3460
|
+
}
|
|
3461
|
+
/**
|
|
3462
|
+
* Asserts that a custom emoji (by id) belongs to the given guild.
|
|
3463
|
+
* Used when reacting in guild channels to reject emojis from other servers.
|
|
3464
|
+
* @throws FluxerError with EMOJI_NOT_IN_GUILD if the emoji is not in the guild
|
|
3465
|
+
*/
|
|
3466
|
+
async assertEmojiInGuild(emojiId, guildId) {
|
|
3467
|
+
const emojis = await this.rest.get(Routes20.guildEmojis(guildId));
|
|
3468
|
+
const list = Array.isArray(emojis) ? emojis : Object.values(emojis ?? {});
|
|
3469
|
+
const found = list.some((e) => e.id === emojiId);
|
|
3470
|
+
if (!found) {
|
|
3471
|
+
throw new FluxerError("Custom emoji is from another server. Use an emoji from this server.", {
|
|
3472
|
+
code: ErrorCodes.EmojiNotInGuild
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
/**
|
|
3477
|
+
* Fetch instance discovery info (API URL, gateway URL, features).
|
|
3478
|
+
* Uses canonical GET /.well-known/fluxer.
|
|
3479
|
+
* Does not require authentication.
|
|
3480
|
+
*/
|
|
3481
|
+
async fetchInstance() {
|
|
3482
|
+
return this.rest.get(Routes20.instanceDiscovery(), { auth: false });
|
|
3483
|
+
}
|
|
3484
|
+
/**
|
|
3485
|
+
* Fetch gateway connection metadata (WebSocket URL, recommended shards, session limits).
|
|
3486
|
+
* Uses authenticated GET /gateway/bot.
|
|
3487
|
+
*/
|
|
3488
|
+
async fetchGatewayInfo() {
|
|
3489
|
+
return this.rest.get(Routes20.gatewayBot());
|
|
3490
|
+
}
|
|
3491
|
+
/**
|
|
3492
|
+
* Fetch a message by channel and message ID. Use when you have IDs (e.g. from a DB).
|
|
3493
|
+
* @param channelId - Snowflake of the channel
|
|
3494
|
+
* @param messageId - Snowflake of the message
|
|
3495
|
+
* @returns The message
|
|
3496
|
+
* @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
|
|
3497
|
+
* @deprecated Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.resolve(channelId))?.messages?.fetch(messageId)
|
|
3498
|
+
* @example
|
|
3499
|
+
* const channel = await client.channels.resolve(channelId);
|
|
3500
|
+
* const message = await channel?.messages?.fetch(messageId);
|
|
3501
|
+
*/
|
|
3502
|
+
async fetchMessage(channelId, messageId) {
|
|
3503
|
+
emitDeprecationWarning3(
|
|
3504
|
+
"Client.fetchMessage()",
|
|
3505
|
+
"Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.resolve(channelId))?.messages?.fetch(messageId)"
|
|
3506
|
+
);
|
|
3507
|
+
return this.channels.fetchMessage(channelId, messageId);
|
|
3508
|
+
}
|
|
3509
|
+
/**
|
|
3510
|
+
* Send a message to any channel by ID. Shorthand for client.channels.send().
|
|
3511
|
+
* Works even when the channel is not cached.
|
|
3512
|
+
* @deprecated Use client.channels.send(channelId, payload).
|
|
3513
|
+
*/
|
|
3514
|
+
async sendToChannel(channelId, payload) {
|
|
3515
|
+
emitDeprecationWarning3(
|
|
3516
|
+
"Client.sendToChannel()",
|
|
3517
|
+
"Use client.channels.send(channelId, payload)."
|
|
3518
|
+
);
|
|
3519
|
+
return this.channels.send(channelId, payload);
|
|
3520
|
+
}
|
|
3521
|
+
/**
|
|
3522
|
+
* Get the message cache for a channel. Returns null if message caching is disabled.
|
|
3523
|
+
* Used by MessageManager.get() and event handlers.
|
|
3524
|
+
* @internal
|
|
3525
|
+
*/
|
|
3526
|
+
_getMessageCache(channelId) {
|
|
3527
|
+
const limit = this.options.cache?.messages ?? 0;
|
|
3528
|
+
if (limit <= 0) return null;
|
|
3529
|
+
if (!this._messageCaches) this._messageCaches = /* @__PURE__ */ new Map();
|
|
3530
|
+
let cache = this._messageCaches.get(channelId);
|
|
3531
|
+
if (!cache) {
|
|
3532
|
+
cache = /* @__PURE__ */ new Map();
|
|
3533
|
+
this._messageCaches.set(channelId, cache);
|
|
3534
|
+
}
|
|
3535
|
+
return cache;
|
|
3536
|
+
}
|
|
3537
|
+
/**
|
|
3538
|
+
* Add a message to the channel cache. Evicts oldest (FIFO) when over limit.
|
|
3539
|
+
* @internal
|
|
3540
|
+
*/
|
|
3541
|
+
_addMessageToCache(channelId, data) {
|
|
3542
|
+
const cache = this._getMessageCache(channelId);
|
|
3543
|
+
if (!cache) return;
|
|
3544
|
+
const limit = this.options.cache?.messages ?? 0;
|
|
3545
|
+
if (limit > 0 && cache.size >= limit && !cache.has(data.id)) {
|
|
3546
|
+
const firstKey = cache.keys().next().value;
|
|
3547
|
+
if (firstKey !== void 0) cache.delete(firstKey);
|
|
3548
|
+
}
|
|
3549
|
+
cache.set(data.id, { ...data });
|
|
3550
|
+
}
|
|
3551
|
+
/**
|
|
3552
|
+
* Remove a message from the channel cache.
|
|
3553
|
+
* @internal
|
|
3554
|
+
*/
|
|
3555
|
+
_removeMessageFromCache(channelId, messageId) {
|
|
3556
|
+
this._messageCaches?.get(channelId)?.delete(messageId);
|
|
3557
|
+
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Get or create a User from API data. Caches in client.users.
|
|
3560
|
+
* Updates existing user's username, avatar, etc. when fresh data is provided.
|
|
3561
|
+
*/
|
|
3562
|
+
getOrCreateUser(data) {
|
|
3563
|
+
const existing = this.users.get(data.id);
|
|
3564
|
+
if (existing) {
|
|
3565
|
+
existing._patch(data);
|
|
3566
|
+
return existing;
|
|
3567
|
+
}
|
|
3568
|
+
const user = new User(this, data);
|
|
3569
|
+
this.users.set(user.id, user);
|
|
3570
|
+
return user;
|
|
3571
|
+
}
|
|
3572
|
+
/** WebSocket manager. Throws if not logged in. */
|
|
3573
|
+
get ws() {
|
|
3574
|
+
if (!this._ws) throw new Error("Client is not logged in");
|
|
3575
|
+
return this._ws;
|
|
3576
|
+
}
|
|
3577
|
+
/**
|
|
3578
|
+
* Send a payload to the gateway (e.g. Voice State Update).
|
|
3579
|
+
* @param shardId - Shard ID (use 0 for single-shard)
|
|
3580
|
+
* @param payload - Gateway payload to send
|
|
3581
|
+
*/
|
|
3582
|
+
sendToGateway(shardId, payload) {
|
|
3583
|
+
this.ws.send(shardId, payload);
|
|
3584
|
+
}
|
|
3585
|
+
async handleDispatch(payload) {
|
|
3586
|
+
if (payload.op !== 0 || !payload.t) return;
|
|
3587
|
+
const { t: event, d } = payload;
|
|
3588
|
+
try {
|
|
3589
|
+
const handler = eventHandlers.get(event);
|
|
3590
|
+
if (handler) await handler(this, d);
|
|
3591
|
+
} catch (err) {
|
|
3592
|
+
this.emit(Events.Error, err instanceof Error ? err : new Error(String(err)));
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Connect to the Fluxer gateway and authenticate.
|
|
3597
|
+
* @param token - Bot token (e.g. from FLUXER_BOT_TOKEN)
|
|
3598
|
+
*/
|
|
3599
|
+
async login(token) {
|
|
3600
|
+
if (this._ws) {
|
|
3601
|
+
throw new FluxerError("Client is already logged in. Call destroy() first.", {
|
|
3602
|
+
code: ErrorCodes.AlreadyLoggedIn
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3605
|
+
this.rest.setToken(token);
|
|
3606
|
+
let intents = this.options.intents ?? 0;
|
|
3607
|
+
if (intents !== 0) {
|
|
3608
|
+
if (!this.options.suppressIntentWarning) {
|
|
3609
|
+
if (typeof process !== "undefined" && process.emitWarning) {
|
|
3610
|
+
process.emitWarning("Fluxer does not support intents yet. Value has been set to 0.", {
|
|
3611
|
+
type: "FluxerIntents"
|
|
3612
|
+
});
|
|
3613
|
+
} else {
|
|
3614
|
+
console.warn("Fluxer does not support intents yet. Value has been set to 0.");
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
intents = 0;
|
|
3618
|
+
}
|
|
3619
|
+
this._ws = new WebSocketManager({
|
|
3620
|
+
token,
|
|
3621
|
+
intents,
|
|
3622
|
+
presence: this.options.presence,
|
|
3623
|
+
rest: { get: (route) => this.rest.get(route) },
|
|
3624
|
+
version: this.options.rest?.version ?? "1",
|
|
3625
|
+
WebSocket: this.options.WebSocket
|
|
3626
|
+
});
|
|
3627
|
+
this._ws.on("dispatch", ({ payload }) => {
|
|
3628
|
+
this.handleDispatch(payload).catch(
|
|
3629
|
+
(err) => this.emit(Events.Error, err instanceof Error ? err : new Error(String(err)))
|
|
3630
|
+
);
|
|
3631
|
+
});
|
|
3632
|
+
this._ws.on(
|
|
3633
|
+
"ready",
|
|
3634
|
+
async ({
|
|
3635
|
+
data
|
|
3636
|
+
}) => {
|
|
3637
|
+
this.user = new ClientUser(this, data.user);
|
|
3638
|
+
const waitForGuilds = this.options.waitForGuilds === true;
|
|
3639
|
+
const pending = waitForGuilds ? /* @__PURE__ */ new Set() : null;
|
|
3640
|
+
for (const g of data.guilds ?? []) {
|
|
3641
|
+
if (g.unavailable === true) {
|
|
3642
|
+
if (pending !== null && g.id) pending.add(g.id);
|
|
3643
|
+
continue;
|
|
3644
|
+
}
|
|
3645
|
+
const guildData = normalizeGuildPayload(g);
|
|
3646
|
+
if (!guildData) continue;
|
|
3647
|
+
const guild = new Guild(this, guildData);
|
|
3648
|
+
this.guilds.set(guild.id, guild);
|
|
3649
|
+
const withCh = g;
|
|
3650
|
+
for (const ch of withCh.channels ?? []) {
|
|
3651
|
+
const channel = Channel.from(this, ch);
|
|
3652
|
+
if (channel) {
|
|
3653
|
+
this.channels.set(channel.id, channel);
|
|
3654
|
+
guild.channels.set(channel.id, channel);
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
if (withCh.voice_states?.length) {
|
|
3658
|
+
this.emit(Events.VoiceStatesSync, {
|
|
3659
|
+
guildId: guild.id,
|
|
3660
|
+
voiceStates: withCh.voice_states
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (pending !== null && pending.size > 0) {
|
|
3665
|
+
this._pendingGuildIds = pending;
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
if (waitForGuilds && (data.guilds ?? []).length === 0) {
|
|
3669
|
+
const GUILD_STREAM_SETTLE_MS = 500;
|
|
3670
|
+
this._guildStreamSettleTimeout = setTimeout(() => {
|
|
3671
|
+
this._guildStreamSettleTimeout = null;
|
|
3672
|
+
this._finalizeReady();
|
|
3673
|
+
}, GUILD_STREAM_SETTLE_MS);
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
this._finalizeReady();
|
|
3677
|
+
}
|
|
3678
|
+
);
|
|
3679
|
+
this._ws.on("error", ({ error }) => this.emit(Events.Error, error));
|
|
3680
|
+
this._ws.on("debug", (msg) => this.emit(Events.Debug, msg));
|
|
3681
|
+
await this._ws.connect();
|
|
3682
|
+
return token;
|
|
3683
|
+
}
|
|
3684
|
+
/**
|
|
3685
|
+
* Called when all guilds have been received (or immediately if not waiting).
|
|
3686
|
+
* Sets readyAt, emits Ready, clears pending state.
|
|
3687
|
+
*/
|
|
3688
|
+
_finalizeReady() {
|
|
3689
|
+
this._pendingGuildIds = null;
|
|
3690
|
+
this.readyAt = /* @__PURE__ */ new Date();
|
|
3691
|
+
this.emit(Events.Ready);
|
|
3692
|
+
}
|
|
3693
|
+
/**
|
|
3694
|
+
* Called by GUILD_CREATE handler when waitForGuilds is enabled.
|
|
3695
|
+
* Removes guild from pending set; when empty, finalizes ready.
|
|
3696
|
+
*/
|
|
3697
|
+
_onGuildReceived(guildId) {
|
|
3698
|
+
const pending = this._pendingGuildIds;
|
|
3699
|
+
if (pending === null) return;
|
|
3700
|
+
pending.delete(guildId);
|
|
3701
|
+
if (pending.size === 0) this._finalizeReady();
|
|
3702
|
+
}
|
|
3703
|
+
/** Disconnect from the gateway and clear cached data. */
|
|
3704
|
+
async destroy() {
|
|
3705
|
+
if (this._guildStreamSettleTimeout !== null) {
|
|
3706
|
+
clearTimeout(this._guildStreamSettleTimeout);
|
|
3707
|
+
this._guildStreamSettleTimeout = null;
|
|
3708
|
+
}
|
|
3709
|
+
if (this._ws) {
|
|
3710
|
+
this._ws.destroy();
|
|
3711
|
+
this._ws = null;
|
|
3712
|
+
}
|
|
3713
|
+
this.rest.setToken(null);
|
|
3714
|
+
this.user = null;
|
|
3715
|
+
this.readyAt = null;
|
|
3716
|
+
this._pendingGuildIds = null;
|
|
3717
|
+
this.guilds.clear();
|
|
3718
|
+
this.channels.clear();
|
|
3719
|
+
this.users.clear();
|
|
3720
|
+
}
|
|
3721
|
+
/** Returns true if the client has received Ready and `user` is set. */
|
|
3722
|
+
isReady() {
|
|
3723
|
+
return this.readyAt !== null && this.user !== null;
|
|
3724
|
+
}
|
|
3725
|
+
/**
|
|
3726
|
+
* Throws if the client is not ready. Use before accessing client.user or other post-ready state.
|
|
3727
|
+
* @throws FluxerError with CLIENT_NOT_READY if client has not received Ready yet
|
|
3728
|
+
*/
|
|
3729
|
+
assertReady() {
|
|
3730
|
+
if (!this.isReady()) {
|
|
3731
|
+
throw new FluxerError(
|
|
3732
|
+
"Client is not ready yet. Wait for the Ready event before accessing client.user.",
|
|
3733
|
+
{
|
|
3734
|
+
code: ErrorCodes.ClientNotReady
|
|
3735
|
+
}
|
|
3736
|
+
);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
static get Routes() {
|
|
3740
|
+
return Routes20;
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3744
|
+
// src/index.ts
|
|
3745
|
+
import { EmbedBuilder as EmbedBuilder3, MessagePayload, AttachmentBuilder } from "@erinjs/builders";
|
|
3746
|
+
import { Routes as Routes21, GatewayOpcodes, MessageAttachmentFlags } from "@erinjs/types";
|
|
3747
|
+
import { resolveTenorToImageUrl, parseUserMention, parsePrefixCommand } from "@erinjs/util";
|
|
3748
|
+
import {
|
|
3749
|
+
PermissionsBitField as PermissionsBitField2,
|
|
3750
|
+
PermissionFlags as PermissionFlags5,
|
|
3751
|
+
resolvePermissionsToBitfield as resolvePermissionsToBitfield3,
|
|
3752
|
+
UserFlagsBitField,
|
|
3753
|
+
UserFlagsBits
|
|
3754
|
+
} from "@erinjs/util";
|
|
3755
|
+
export {
|
|
3756
|
+
AttachmentBuilder,
|
|
3757
|
+
Base,
|
|
3758
|
+
CDN_URL,
|
|
3759
|
+
CategoryChannel,
|
|
3760
|
+
Channel,
|
|
3761
|
+
ChannelManager,
|
|
3762
|
+
Client,
|
|
3763
|
+
ClientUser,
|
|
3764
|
+
DMChannel,
|
|
3765
|
+
EmbedBuilder3 as EmbedBuilder,
|
|
3766
|
+
ErrorCodes,
|
|
3767
|
+
Events,
|
|
3768
|
+
FluxerError,
|
|
3769
|
+
GatewayOpcodes,
|
|
3770
|
+
Guild,
|
|
3771
|
+
GuildBan,
|
|
3772
|
+
GuildChannel,
|
|
3773
|
+
GuildEmoji,
|
|
3774
|
+
GuildMember,
|
|
3775
|
+
GuildMemberManager,
|
|
3776
|
+
GuildMemberRoleManager,
|
|
3777
|
+
GuildSticker,
|
|
3778
|
+
Invite,
|
|
3779
|
+
LinkChannel,
|
|
3780
|
+
Message,
|
|
3781
|
+
MessageAttachmentFlags,
|
|
3782
|
+
MessageCollector,
|
|
3783
|
+
MessageManager,
|
|
3784
|
+
MessagePayload,
|
|
3785
|
+
MessageReaction,
|
|
3786
|
+
PermissionFlags5 as PermissionFlags,
|
|
3787
|
+
PermissionsBitField2 as PermissionsBitField,
|
|
3788
|
+
ReactionCollector,
|
|
3789
|
+
Role,
|
|
3790
|
+
Routes21 as Routes,
|
|
3791
|
+
STATIC_CDN_URL,
|
|
3792
|
+
TextChannel,
|
|
3793
|
+
User,
|
|
3794
|
+
UserFlagsBitField,
|
|
3795
|
+
UserFlagsBits,
|
|
3796
|
+
UsersManager,
|
|
3797
|
+
VoiceChannel,
|
|
3798
|
+
Webhook,
|
|
3799
|
+
cdnAvatarURL,
|
|
3800
|
+
cdnBannerURL,
|
|
3801
|
+
cdnDefaultAvatarURL,
|
|
3802
|
+
cdnDisplayAvatarURL,
|
|
3803
|
+
cdnMemberAvatarURL,
|
|
3804
|
+
cdnMemberBannerURL,
|
|
3805
|
+
parsePrefixCommand,
|
|
3806
|
+
parseUserMention,
|
|
3807
|
+
resolvePermissionsToBitfield3 as resolvePermissionsToBitfield,
|
|
3808
|
+
resolveTenorToImageUrl
|
|
3809
|
+
};
|